Creando el patrón de diseño Singleton en PHP5

Resuelto Andrew Moore asked hace 54 años • 22 respuestas

¿Cómo se crearía una clase Singleton usando clases PHP5?

Andrew Moore avatar Jan 01 '70 08:01 Andrew Moore
Aceptado
/**
 * 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.

 avatar Oct 15 '2008 00:10

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";
mpartel avatar Apr 08 '2013 02:04 mpartel

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.

selfawaresoup avatar Dec 21 '2009 10:12 selfawaresoup

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.

Abraham Tugalov avatar Jun 13 '2016 22:06 Abraham Tugalov