Home >Backend Development >PHP Tutorial >Introduction to PHP Design Patterns IV Singleton Pattern_PHP Tutorial

Introduction to PHP Design Patterns IV Singleton Pattern_PHP Tutorial

WBOY
WBOYOriginal
2016-07-13 10:30:05899browse

In almost all object-oriented programs, one or two resources are always created and continue to be shared and used in the program application. For example, such a resource is used in the database connection of an e-commerce program: this connection is initialized when the application starts, and the program can then be executed effectively; when the program ends, the connection is eventually disconnected and destroyed. If you write the code, there is no need to create a database connection every time, which is very inefficient. Established connections should be easily reusable by your code. The question is, how would you make this database connection based on the above requirements? (Or connect to other unique resources that are recycled, such as an open file or a queue.)

Question

How do you ensure that an instance of a particular class is unique (it is the only instance of that class) and that it is easily accessible?

Solution

Of course, global variables are the obvious solution. But it's like Pandora's box (right judgment comes from experience, and wrong judgment comes from experience. That's what the proverb means.), any of your code can modify global variables, which will inevitably cause more debugging Accident. In other words, there will always be some problems with the state of global variables (here is a good description of the problems with global variable usage, http://c2.com/cgi/wiki?GlobalVariablesAreBad).

Use this pattern called Singleton when you need a unique instance of a particular class. A class based on the Singleton pattern can instantiate and initialize an instance of this class and provide absolutely the same connection every time. Generally, it is implemented using a static method named getInstance().

The key question is how to obtain an accurate and unified instance at every moment. Please see the example below:

// PHP4

Function TestGetInstance() {

$this->assertIsA(

$obj1 =& DbConn::getInstance(),

‘DbConn’,

 ‘The returned object is an instance of DbConn’);

$this->assertReference(

$obj1,

$obj2 =& DbConn::getInstance(),

 ‘Two calls to getInstance() return the same object’);

 }

Comment: assertReference

The assertReference() method ensures that the two parameters passed refer to the same PHP variable.

In PHP4, it is asserted that the two tested parameters are the same object. The assertReference() method may not be recommended after being ported to PHP5.

This test method has two assertions: the first one determines that the value returned by the first call to the static method DbConn::getInstance() is an instance of the DbConn object, and the second one is used to determine the value returned by the second call to the getInstance() method. The values ​​refer to the same object instance, which means they use the same object.

In addition to asserting the expected execution results of the code, Test also indicates the correct usage of getInstance() (PHP4): $local_conn_var=&DbConn::getInstance(). The return value of the reference (=&) static method is assigned to this local variable.

Write another piece of test code: directly using "new" to instantiate a singleton class will cause certain types of errors. The test code is as follows:

Function TestBadInstantiate() {

$obj =& new DbConn;

$this->assertErrorPattern(

 ‘/(bad|nasty|evil|do not|don’t|warn).*’.

‘(instance|create|new|direct)/i’);

 }

This code directly creates an instance of DbConn, which will cause PHP to report an error. In order to make the code more stable, we use PCRE regular expressions to match error messages. (The exact wording of the error message displayed is not important.)

 [next]

Sample code

Singleton mode is a very interesting mode. Let us explore its implementation process using PHP4 and PHP5, starting with PHP4 now.

Global approach

Theoretically, a global variable can generate a perfect singleton, but the global variable may be modified: during the running of the code, there is no guarantee that the global variable points to an object. Therefore, by not allowing global variables to be directly referenced globally, the problem of "too random access" to global variables can be reduced. For example, this code "hides" references to global variables by using a very long and unique name.

class DbConn {

Function DbConn($fromGetInstance=false) {

 if (M_E != $fromGetInstance) {

trigger_error(‘The DbConn class is a Singleton,’

 .’ please do not instantiate directly.’);

 }

 }

Function &getInstance() {

$key = ‘__some_unique_key_for_the_DbConn_instance__’;

 if (!(array_key_exists($key, $GLOBALS) && is_object($GLOBALS[$key])

 && ‘dbconn’ == get_class($GLOBALS[$key]) )) {

$GLOBALS[$key] =& new DbConn(M_E);

 }

return $GLOBALS[$key];

 }

 }

In the constructor of DbConn, you may be confused about the default parameters of $fromGetInstance. It provides (very weak) protection when the object is instantiated directly: unless the default value becomes e (M_E = 2.718281828459 in PHP's mathematical constants), this code will report an error.

Represented as a UML class diagram, the solution is as follows:

If you do not choose this "mysterious parameter" - type protection, creating a global tag is another option, and use it to verify that you created the object through the getInstance() method. The conservation approach changes from "you know its name" to "it's in the environment."

Here is an example that explains why constructor protection code has a global flag:

class DbConn {

Function DbConn() {

$token = ‘__some_DbConn_instance_create_semaphore__’;

 if (!array_key_exists($token, $GLOBALS)) {

trigger_error(‘The DbConn class is a Singleton,’

 .’ please do not instantiate directly.’);

 }

 }

Function &getInstance() {

static $instance = array();

 if (!$instance) {

$token = ‘__some_DbConn_instance_create_semaphore__’;

$GLOBALS[$token] = true;

 $instance[0] =& new DbConn;

unset($GLOBALS[$token]);

 }

Tips

PHP4 allows you to change the value of $this in the constructor. In the past, we would be used to setting $this = null; when there is a create constructor error to ensure that the invalid object cannot be used by the code. Things that were useful in PHP4 are not compatible in PHP5 and will be proven in your code in the future. This technique is no longer recommended.

Another important point in this code is the usage of reference operation &. There are two places where you need to use &. The first one is used before the function name when defining a function to indicate that a reference will be returned. The second is to assign the new DbConn object to the $GLOBALS array. (Mentioned in the Preface and Value Objects chapter: In PHP4, you always use the & operator to create, pass and return objects by reference,)

The conditional check of the getInstance() method is often written to run without warning, even at the E_ALL error level. It checks whether there is a DbConn object at the appropriate location in the $GLOBAL array, and if not, creates the object there. This method then returns the result that the object can be created repeatedly or that the object has been created by this method before. When the method ends, you can confirm that you have a valid instance of the class and that it has been effectively initialized.

 [next]

Static mode

Regarding the problem of global variables, it even exists in the global variables hidden in getInstance(). Because global variables are valid anywhere in the script, you can still corrupt the global variable without noticing,

It is a clean way to use static variables to store Singleton inside the getInstance() method. The first code snippet is as follows:

class DbConn {

 // ...

Function &getInstance() {

static $instance = false;

 if (!$instance) $instance =& new DbConn(M_E);

return $instance;

 }

 }

The Zend 1 engine cannot store references to static variables in PHP4 (see http://www.php.net/manual/en/language.variables.scope.php#AEN3609). Use a workspace to store the static array and place a reference to the singleton instance into a known array. The getInstance() method is as follows:

class DbConn {

Function DbConn($fromGetInstance=false) {

 if (M_E != $fromGetInstance) {

trigger_error(‘The DbConn class is a Singleton,’

 .’ please do not instantiate directly.’);

 }

 }

Function &getInstance() {

static $instance = array();

 if (!$instance) $instance0 =& new DbConn(M_E);

return $instance0;

 }

 }

This code simply selects the first element of the static array $instancede to hold a reference to the single DbConns instance.

Although this code relies a bit on PHP's Boolean method, it is more rigorous than the global version: when testing conditions, using an empty array will result in false. Just like in the previous version of the DbConn class, reference operators are required in the definition and assignment parts of the function.

Singleton mode in PHP5

It is easier to implement the singleton mode in PHP5. PHP5’s access control to internal variables and functions of the class has been strengthened. By setting the DbConn::_construct() constructor to private, this class cannot be instantiated directly. Represented by UML diagram, PHP5’s DbConn singleton mode is as follows:

Use a combination of static methods and static variables to maintain this instance, and set the constructor to be private to prevent direct instantiation of the class from creating an instance. The code is as follows:

class DbConn {

 /**

  * static property to hold singleton instance

  */

static $instance = false;

 /**

  * constructor

  * private so only getInstance() method can instantiate

  * @return void

  */

 private function __construct() {}

 /**

  * factory method to return the singleton instance

  * @return DbConn

  */

public function getInstance() {

 if (!DbConn::$instance) {

DbConn::$instance = new DbConn;

 }

return DbConn::$instance;

 }

 }

 Note: For more exciting articles, please pay attention to the Programming Tutorial column of Bangke Home.

www.bkjia.comtruehttp: //www.bkjia.com/PHPjc/767405.htmlTechArticleIn almost all object-oriented programs, one or two resources are always created and continuously shared in the application. use. For example, such a resource, in an e-commerce program...
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn