Creando el patrón de diseño Singleton en PHP5
¿Cómo se crearía una clase Singleton usando clases PHP5?
/**
* Singleton class
*
*/
final class UserFactory
{
private static $inst = null;
// Prevent cloning and de-serializing
private function __clone(){}
private function __wakeup(){}
/**
* Call this method to get singleton
*
* @return UserFactory
*/
public static function Instance()
{
if ($inst === null) {
$inst = new UserFactory();
}
return $inst;
}
/**
* Private ctor so nobody else can instantiate it
*
*/
private function __construct()
{
}
}
Usar:
$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();
$fact == $fact2;
Pero:
$fact = new UserFactory()
Lanza un error.
Consulte http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static para comprender los alcances de las variables estáticas y por qué static $inst = null;
funciona la configuración.
Desafortunadamente, la respuesta de Inwdr falla cuando hay varias subclases.
Aquí hay una clase base Singleton heredable correcta.
class Singleton
{
private static $instances = array();
protected function __construct() {}
protected function __clone() {}
public function __wakeup()
{
throw new Exception("Cannot unserialize singleton");
}
public static function getInstance()
{
$cls = get_called_class(); // late-static-bound class name
if (!isset(self::$instances[$cls])) {
self::$instances[$cls] = new static;
}
return self::$instances[$cls];
}
}
Código de prueba:
class Foo extends Singleton {}
class Bar extends Singleton {}
echo get_class(Foo::getInstance()) . "\n";
echo get_class(Bar::getInstance()) . "\n";
PHP 5.3 permite la creación de una clase Singleton heredable mediante enlace estático tardío:
class Singleton
{
protected static $instance = null;
protected function __construct()
{
//Thou shalt not construct that which is unconstructable!
}
protected function __clone()
{
//Me not like clones! Me smash clones!
}
public static function getInstance()
{
if (!isset(static::$instance)) {
static::$instance = new static;
}
return static::$instance;
}
}
Esto resuelve el problema de que antes de PHP 5.3, cualquier clase que extendiera un Singleton produciría una instancia de su clase principal en lugar de la suya propia.
Ahora puedes hacer:
class Foobar extends Singleton {};
$foo = Foobar::getInstance();
Y $foo será una instancia de Foobar en lugar de una instancia de Singleton.
La forma real y moderna de hacer un patrón Singleton es:
<?php
/**
* Singleton Pattern.
*
* Modern implementation.
*/
class Singleton
{
/**
* Call this method to get singleton
*/
public static function instance()
{
static $instance = false;
if( $instance === false )
{
// Late static binding (PHP 5.3+)
$instance = new static();
}
return $instance;
}
/**
* Make constructor private, so nobody can call "new Class".
*/
private function __construct() {}
/**
* Make clone magic method private, so nobody can clone instance.
*/
private function __clone() {}
/**
* Make sleep magic method private, so nobody can serialize instance.
*/
private function __sleep() {}
/**
* Make wakeup magic method private, so nobody can unserialize instance.
*/
private function __wakeup() {}
}
Entonces ahora puedes usarlo como.
<?php
/**
* Database.
*
* Inherited from Singleton, so it's now got singleton behavior.
*/
class Database extends Singleton {
protected $label;
/**
* Example of that singleton is working correctly.
*/
public function setLabel($label)
{
$this->label = $label;
}
public function getLabel()
{
return $this->label;
}
}
// create first instance
$database = Database::instance();
$database->setLabel('Abraham');
echo $database->getLabel() . PHP_EOL;
// now try to create other instance as well
$other_db = Database::instance();
echo $other_db->getLabel() . PHP_EOL; // Abraham
$other_db->setLabel('Priler');
echo $database->getLabel() . PHP_EOL; // Priler
echo $other_db->getLabel() . PHP_EOL; // Priler
Como puede ver, esta realización es mucho más flexible.