note 87941 modified in language.oop5.patterns by danbrown
| From: | danbrown@php.net | Date: | Sun, 23 Jan 2011 16:35:58 +0000 |
| Subject: | note 87941 modified in language.oop5.patterns by danbrown | ||
| References: | 1 | Groups: | php.notes |
| Request: | Send a blank email to php-notes+get-176093@lists.php.net to get a copy of this message | ||
1: The singleton pattern ensures that only one instance of a class is ever created. Thus, applying
it to a database connection object is common, since reusing a connection reduces latency and slows
down reaching the database server's connection limit. If a database connection is only made in
the singleton's constructor then it is probably impossible to accidentally open another
connection via that object.
I say "probably impossible" because there are still some other issues: in this example, an
invalid database connection identifier can be stored, retrieved, or lost (since it is an
unserializable resource type) via object serialization without careful attention to the __sleep and
__wakeup magic methods (and probably won't be valid on deserialization even if it is retained),
and the __clone magic method should be implemented in a way that prevents object duplication.
(This example also ignores database connection pooling to stay focused on singletons.)
2: A singleton does not contain itself; it contains a reference to itself, which is conceptually
similar to a pointer from the C language or a reference from Java. (In fact these are all very
different under the hood and in practice, but I will ignore this for simplicity.)
Consider the following typical PHP 5.0 to 5.2 implementation:
<?php
class Example {
private static $instance;
private __construct() {
// Do important initializations here, like normal.
}
public static function getInstance() {
if (null == self::$instance):
// First invocation only.
$className = __CLASS__;
self::$instance = new $className();
endif;
return self::$instance;
}
public function __clone() {
throw new Exception('Illegally attempted to clone ' . __CLASS__);
}
}
?>
Static class member variables can exist even without an instance of their class, and static class
methods can be invoked without a class instance as well. The member variable that holds a reference
to the singleton class instance ($instance) is static, as is the method to instantiate a singleton
object (Example::getInstance()). Note that the variable $instance is private, so it cannot be
affected by anything external to the class (thus preventing outside code from creating an instance
of Example).
Static member variables are not really "inside" a class; each class instance points to the
same static member variables as other instances, "sharing" these static variables between
them (in a way) even before any class instances exist. Thus, Example::getInstance() always checks
the same $instance variable.
3: The constructor has an access specifier of "private" to prevent direct instantiation
using the "new" operator. (Prior to the implementation of the __callStatic features of PHP
5.3 it is not useful to mark the constructor "protected" since subclasses won't
automatically also be singletons.) However, private methods can be invoked from a class's other
methods; the static method Example::getInstance() is therefore allowed to invoke the private
constructor. On first invocation, it creates an instance of Example and stores a reference to it in
$instance. Subsequently, it just returns this reference.
4: The singleton pattern makes a class (slightly) more complicated, and the overhead of invoking its
instantiation method is probably slightly higher than accessing a global variable instead. However,
the benefits are substantial from an architectural perspective: it helps prevent performance losses
due to accidentally instantiating more than one heavyweight class, it helps prevent bugs due to
accidentally instantiating more than one instance of a class (such as a class that renders the
requested HTML document in an online content management system), and helps keeps your global
namespace and other lexical scopes clean even prior to the introduction of proper namespaces in PHP
5.3 (thus helping to prevent accidental variable clobbering or unexpected initial variable values
from a previous module).
--was--
nbcarts:
1: The singleton pattern ensures that only one instance of a class is ever created. Thus, applying
it to a database connection object is common, since reusing a connection reduces latency and slows
down reaching the database server's connection limit. If a database connection is only made in
the singleton's constructor then it is probably impossible to accidentally open another
connection via that object.
I say "probably impossible" because there are still some other issues: in this example, an
invalid database connection identifier can be stored, retrieved, or lost (since it is an
unserializable resource type) via object serialization without careful attention to the __sleep and
__wakeup magic methods (and probably won't be valid on deserialization even if it is retained),
and the __clone magic method should be implemented in a way that prevents object duplication.
(This example also ignores database connection pooling to stay focused on singletons.)
2: A singleton does not contain itself; it contains a reference to itself, which is conceptually
similar to a pointer from the C language or a reference from Java. (In fact these are all very
different under the hood and in practice, but I will ignore this for simplicity.)
Consider the following typical PHP 5.0 to 5.2 implementation:
<?php
class Example {
private static $instance;
private __construct() {
// Do important initializations here, like normal.
}
public static function getInstance() {
if (null == self::$instance):
// First invocation only.
$className = __CLASS__;
self::$instance = new $className();
endif;
return self::$instance;
}
public function __clone() {
throw new Exception('Illegally attempted to clone ' . __CLASS__);
}
}
?>
Static class member variables can exist even without an instance of their class, and static class
methods can be invoked without a class instance as well. The member variable that holds a reference
to the singleton class instance ($instance) is static, as is the method to instantiate a singleton
object (Example::getInstance()). Note that the variable $instance is private, so it cannot be
affected by anything external to the class (thus preventing outside code from creating an instance
of Example).
Static member variables are not really "inside" a class; each class instance points to the
same static member variables as other instances, "sharing" these static variables between
them (in a way) even before any class instances exist. Thus, Example::getInstance() always checks
the same $instance variable.
3: The constructor has an access specifier of "private" to prevent direct instantiation
using the "new" operator. (Prior to the implementation of the __callStatic features of PHP
5.3 it is not useful to mark the constructor "protected" since subclasses won't
automatically also be singletons.) However, private methods can be invoked from a class's other
methods; the static method Example::getInstance() is therefore allowed to invoke the private
constructor. On first invocation, it creates an instance of Example and stores a reference to it in
$instance. Subsequently, it just returns this reference.
4: The singleton pattern makes a class (slightly) more complicated, and the overhead of invoking its
instantiation method is probably slightly higher than accessing a global variable instead. However,
the benefits are substantial from an architectural perspective: it helps prevent performance losses
due to accidentally instantiating more than one heavyweight class, it helps prevent bugs due to
accidentally instantiating more than one instance of a class (such as a class that renders the
requested HTML document in an online content management system), and helps keeps your global
namespace and other lexical scopes clean even prior to the introduction of proper namespaces in PHP
5.3 (thus helping to prevent accidental variable clobbering or unexpected initial variable values
from a previous module).
http://php.net/manual/en/language.oop5.patterns.php