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

204

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

Andrew Moore
fuente
1
@ Andrew Dont instancia una segunda instancia que se conecta a la base de datos en ese momento. Pase esa instancia a donde se necesita. La necesidad de un Singleton es un olor a código. Más en gooh.posterous.com/singletons-in-php
Gordon
3
@ Andrew Mmmmkay. Sin ofender, pero le sugiero que obtenga un libro sobre la calidad del software antes de continuar esta discusión. Los singletons no simplifican sino que complican el mantenimiento y el desarrollo normales. De hecho, es al revés: son pruebas unitarias que simplifican y permiten el desarrollo en primer lugar.
Gordon
3
@ Andrew: Asumes ahora que solo necesitas una conexión de base de datos. ¿Qué sucede cuando cambian sus requisitos y realmente necesita hablar con 2 servidores de bases de datos? Sin mencionar que si no puedes confiar en que tu equipo haga las cosas bien , crear un singleton no te ayudará en lo más mínimo. Haga las cosas bien desde el principio y obtenga un equipo en el que pueda confiar y estará bien.
ircmaxell
44
El hecho de que el Singleton haya sido usado en exceso no lo convierte en un mal patrón que debe evitarse. No odies en el Singleton. A veces es una solución perfectamente buena para un determinado problema. Mejor comience a discutir por qué no deberíamos usarlo en lugar de solo tratar de degradarlo emocionalmente.
Gilles Lesire

Respuestas:

268
/**
 * Singleton class
 *
 */
final class UserFactory
{
    /**
     * Call this method to get singleton
     *
     * @return UserFactory
     */
    public static function Instance()
    {
        static $inst = null;
        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 ámbitos de variables estáticas y por qué la configuración static $inst = null;funciona.

Arount
fuente
59
para comparar las dos instancias, debe usar === en lugar de ==. == devolverá verdadero si $ fact1 y $ fact2 son ambos de la misma clase, pero === solo devuelve verdadero si ambos son la misma instancia del mismo objeto.
Keith Twombley
10
El método de clonación también debe ser privado
Alex Petrov,
22
¿Este método no restablecerá la instancia de UserFactory a nulo cada vez que llame a Instance ()? En java, la variable $ inst sería un atributo estático privado que no debería reiniciarse una y otra vez, de lo contrario, es mejor que no lo convierta en un singleton.
Rudy Garcia
8
Aquí hay una buena descripción de por qué y cómo declarar la variable como estática en la función funciona como el autor pretende: php.net/manual/en/…
hereswhatidid
10
Debería usar $ inst = new self (); no $ inst = nueva UserFactory (); para cualquiera que se encuentre con esto más tarde. +1 por usar una metodología PHP integrada.
Ligemer
119

PHP 5.3 permite la creación de una clase Singleton heredable a través del 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, que antes de PHP 5.3 cualquier clase que extendiera un Singleton produciría una instancia de su clase padre en lugar de la suya.

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
fuente
1
El enlace estático tardío es de hecho algo muy bueno en php 5.3. Lástima que todavía no puedo usarlo.
AntonioCS
44
De @ggsonic: "subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());".
Brock Adams
44
Esto no funciona en absoluto, ¿sucede que Foobar fue la primera clase que construiste?
Chris KL
1
todavía existe la posibilidad de clonar ..... "$ a = Singleton :: getInstance (); $ b = unserialize (serialize ($ a)); $ a! == $ b;"
bortunac
15
¡Esto no funciona cuando hay más de una subclase! $instancereside en Singleton, no en la subclase. Después de instanciar alguna subclase, getInstance () devolverá esa instancia para todas las subclases.
mpartel
116

Lamentablemente, la respuesta de Inwdr rompe 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
fuente
1
Esto es lo más cercano a la correcta implementación de Singleton. También debe considerar lanzar el método __wakeup () para evitar la falta de serialización .
Robert Rossmann
En realidad, debe lanzar una Excepción o generar un error manualmente: declarar la función como protegida / privada solo generará un E_WARNING que dice que no puede acceder al método, pero de lo contrario continuaría.
Robert Rossmann
Gracias. Normalmente tengo todas las advertencias, etc. convertidas en excepciones, así que olvidé la diferencia cuando probé: P
mpartel
Esta es la única solución que encontré que trata adecuadamente con múltiples subclases. ¡Gracias!
Bob Dankert
36

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() {}

}

Así que 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
fuente
44
Esta es la respuesta más clara sobre el patrón Singleton dentro de este hilo. Gracias.
Gus
He implementado este enfoque y funciona como se esperaba: la segunda instancia se vuelve nula. Sin embargo, no necesitaba extender la clase concreta también. Acabo de implementar Singleton :: instance () en el constructor de esa clase concreta.
snaphuman
en la instancefunción $instancedebe ser nullnofalse
Mifas
Sí, pero no es función, sino método.
Abraham Tugalov
26

Probablemente debería agregar un método privado __clone () para no permitir la clonación de una instancia.

private function __clone() {}

Si no incluye este método, es posible lo siguiente

$inst1=UserFactory::Instance(); // to stick with the example provided above
$inst2=clone $inst1;

ahora $inst1! == $inst2- ya no son la misma instancia.

Stefan Gehrig
fuente
11
<?php
/**
 * Singleton patter in php
 **/
trait SingletonTrait {
   protected static $inst = null;

  /**
   * call this method to get instance
   **/
   public static function getInstance(){
      if (static::$inst === null){
         static::$inst = new static();
      }
      return static::$inst;
  }

  /**
   * protected to prevent clonning 
   **/
  protected function __clone(){
  }

  /**
   * protected so no one else can instance it 
   **/
  protected function __construct(){
  }
}

usar:

/**
 *  example of class definitions using SingletonTrait
 */
class DBFactory {
  /**
   * we are adding the trait here 
   **/
   use SingletonTrait;

  /**
   * This class will have a single db connection as an example
   **/
  protected $db;


 /**
  * as an example we will create a PDO connection
  **/
  protected function __construct(){
    $this->db = 
        new PDO('mysql:dbname=foodb;port=3305;host=127.0.0.1','foouser','foopass');
  }
}
class DBFactoryChild extends DBFactory {
  /**
   * we repeating the inst so that it will differentiate it
   * from UserFactory singleton
   **/
   protected static $inst = null;
}


/**
 * example of instanciating the classes
 */
$uf0 = DBFactoryChild::getInstance();
var_dump($uf0);
$uf1 = DBFactory::getInstance();
var_dump($uf1);
echo $uf0 === $uf1;

responer:

object(DBFactoryChild)#1 (0) {
}
object(DBFactory)#2 (0) {
}

Si está utilizando PHP 5.4: el rasgo es una opción, por lo que no tiene que desperdiciar la jerarquía de herencia para tener el patrón Singleton

y también observe que si usa rasgos o extiende la clase Singleton , un extremo suelto era crear clases únicas de clases secundarias si no agrega la siguiente línea de código:

   protected static $inst = null;

en la clase infantil

El resultado inesperado será:

object(DBFactoryChild)#1 (0) {
}
object(DBFactoryChild)#1 (0) {
}
jose segura
fuente
10
protected  static $_instance;

public static function getInstance()
{
    if(is_null(self::$_instance))
    {
        self::$_instance = new self();
    }
    return self::$_instance;
}

Este código puede aplicarse a cualquier clase sin importar su nombre de clase.

hungneox
fuente
8

Admite múltiples objetos con 1 línea por clase:

Este método impondrá singletons en cualquier clase que desee, todo lo que debe hacer es agregar 1 método a la clase que desea hacer un singleton y esto lo hará por usted.

Esto también almacena objetos en una clase "SingleTonBase" para que pueda depurar todos los objetos que ha utilizado en su sistema recurriendo a los SingleTonBaseobjetos.


¡Cree un archivo llamado SingletonBase.php e inclúyalo en la raíz de su script!

El código es

abstract class SingletonBase
{
    private static $storage = array();

    public static function Singleton($class)
    {
        if(in_array($class,self::$storage))
        {
            return self::$storage[$class];
        }
        return self::$storage[$class] = new $class();
    }
    public static function storage()
    {
       return self::$storage;
    }
}

Luego, para cualquier clase que desee hacer un singleton, simplemente agregue este pequeño método individual.

public static function Singleton()
{
    return SingletonBase::Singleton(get_class());
}

Aquí hay un pequeño ejemplo:

include 'libraries/SingletonBase.resource.php';

class Database
{
    //Add that singleton function.
    public static function Singleton()
    {
        return SingletonBase::Singleton(get_class());
    }

    public function run()
    {
        echo 'running...';
    }
}

$Database = Database::Singleton();

$Database->run();

Y solo puede agregar esta función singleton en cualquier clase que tenga y solo creará 1 instancia por clase.

NOTA: Siempre debe hacer que la construcción __ sea privada para eliminar el uso de la nueva Clase (); instancias

RobertPitt
fuente
5
class Database{

        //variable to hold db connection
        private $db;
        //note we used static variable,beacuse an instance cannot be used to refer this
        public static $instance;

        //note constructor is private so that classcannot be instantiated
        private function __construct(){
          //code connect to database  

         }     

         //to prevent loop hole in PHP so that the class cannot be cloned
        private function __clone() {}

        //used static function so that, this can be called from other classes
        public static function getInstance(){

            if( !(self::$instance instanceof self) ){
                self::$instance = new self();           
            }
             return self::$instance;
        }


        public function query($sql){
            //code to run the query
        }

    }


Access the method getInstance using
$db = Singleton::getInstance();
$db->query();
rizon
fuente
5

Realmente no necesitas usar el patrón Singleton porque se considera un antipatrón. Básicamente, hay muchas razones para no implementar este patrón en absoluto. Lea esto para comenzar con: Las mejores prácticas en clases de singleton PHP .

Si, después de todo, todavía cree que necesita usar el patrón Singleton, podríamos escribir una clase que nos permita obtener la funcionalidad Singleton extendiendo nuestra clase abstracta SingletonClassVendor.

Esto es con lo que vine a resolver este problema.

<?php
namespace wl;


/**
 * @author DevWL
 * @dosc allows only one instance for each extending class.
 * it acts a litle bit as registry from the SingletonClassVendor abstract class point of view
 * but it provides a valid singleton behaviour for its children classes
 * Be aware, the singleton pattern is consider to be an anti-pattern
 * mostly because it can be hard to debug and it comes with some limitations.
 * In most cases you do not need to use singleton pattern
 * so take a longer moment to think about it before you use it.
 */
abstract class SingletonClassVendor
{
    /**
     *  holds an single instance of the child class
     *
     *  @var array of objects
     */
    protected static $instance = [];

    /**
     *  @desc provides a single slot to hold an instance interchanble between all child classes.
     *  @return object
     */
    public static final function getInstance(){
        $class = get_called_class(); // or get_class(new static());
        if(!isset(self::$instance[$class]) || !self::$instance[$class] instanceof $class){
            self::$instance[$class] = new static(); // create and instance of child class which extends Singleton super class
            echo "new ". $class . PHP_EOL; // remove this line after testing
            return  self::$instance[$class]; // remove this line after testing
        }
        echo "old ". $class . PHP_EOL; // remove this line after testing
        return static::$instance[$class];
    }

    /**
     * Make constructor abstract to force protected implementation of the __constructor() method, so that nobody can call directly "new Class()".
     */
    abstract protected 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() {}

}

Use ejemplo:

/**
 * EXAMPLE
 */

/**
 *  @example 1 - Database class by extending SingletonClassVendor abstract class becomes fully functional singleton
 *  __constructor must be set to protected becaouse: 
 *   1 to allow instansiation from parent class 
 *   2 to prevent direct instanciation of object with "new" keword.
 *   3 to meet requierments of SingletonClassVendor abstract class
 */
class Database extends SingletonClassVendor
{
    public $type = "SomeClass";
    protected function __construct(){
        echo "DDDDDDDDD". PHP_EOL; // remove this line after testing
    }
}


/**
 *  @example 2 - Config ...
 */
class Config extends SingletonClassVendor
{
    public $name = "Config";
    protected function __construct(){
        echo "CCCCCCCCCC" . PHP_EOL; // remove this line after testing
    }
}

Solo para demostrar que funciona como se esperaba:

/**
 *  TESTING
 */
$bd1 = Database::getInstance(); // new
$bd2 = Database::getInstance(); // old
$bd3 = Config::getInstance(); // new
$bd4 = Config::getInstance(); // old
$bd5 = Config::getInstance(); // old
$bd6 = Database::getInstance(); // old
$bd7 = Database::getInstance(); // old
$bd8 = Config::getInstance(); // old

echo PHP_EOL."COMPARE ALL DATABASE INSTANCES".PHP_EOL;
var_dump($bd1);
echo '$bd1 === $bd2' . ($bd1 === $bd2)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd2 === $bd6' . ($bd2 === $bd6)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd6 === $bd7' . ($bd6 === $bd7)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE

echo PHP_EOL;

echo PHP_EOL."COMPARE ALL CONFIG INSTANCES". PHP_EOL;
var_dump($bd3);
echo '$bd3 === $bd4' . ($bd3 === $bd4)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd4 === $bd5' . ($bd4 === $bd5)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd5 === $bd8' . ($bd5 === $bd8)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
DevWL
fuente
Mientras leía las respuestas más votadas, tenía algo como esto en mente. Afortunadamente ya estaba aquí :)
hatef
3

Toda esta complejidad ("enlace estático tardío" ... harumph) es, para mí, simplemente un signo del modelo de clase / objeto roto de PHP. Si los objetos de clase fueran objetos de primera clase (ver Python), entonces "$ _instance" sería una instancia de clase variable de : un miembro del objeto de clase, en oposición a un miembro / propiedad de sus instancias, y también en lugar de compartido por sus descendientes En el mundo Smalltalk, esta es la diferencia entre una "variable de clase" y una "variable de instancia de clase".

En PHP, me parece que debemos tomar en serio la guía de que los patrones son una guía para escribir código: quizás podríamos pensar en una plantilla Singleton, pero tratando de escribir código que herede de una clase "Singleton" real parece equivocado para PHP (aunque supuse que un alma emprendedora podría crear una palabra clave SVN adecuada).

Continuaré codificando cada singleton por separado, usando una plantilla compartida.

Tenga en cuenta que estoy absolutamente fuera de la discusión singletons-are-evil, la vida es demasiado corta.

Tom Stambaugh
fuente
Sus comentarios son correctos al ver la complejidad cada vez mayor del lenguaje PHP. Parece que se están agregando demasiadas palabras clave nuevas para salir de muchos agujeros de diseño diferentes en demasiados paradigmas de codificación diferentes. Peor aún, debido a la alta tasa de cambio y la versión sesgada entre los hosts y las plataformas de desarrollo, la "solución del día" de hoy (como los rasgos en la respuesta de @Eric Anderson [ stackoverflow.com/a/23998306/3696363], ) no funcionan en sistemas de producción que podrían estar ejecutando una versión "estable" en lugar de la "última, la mejor".
Eliyahu Skoczylas
2

Sé que esto probablemente causará una guerra de llamas innecesaria, pero puedo ver cómo es posible que desee más de una conexión de base de datos, por lo que reconocería que singleton podría no ser la mejor solución para eso ... sin embargo, hay otros usos del patrón singleton que encuentro extremadamente útiles.

Aquí hay un ejemplo: decidí rodar mi propio MVC y motor de plantillas porque quería algo realmente liviano. Sin embargo, los datos que quiero mostrar contienen muchos caracteres matemáticos especiales como ≥ y μ y lo que tiene ... Los datos se almacenan como el carácter UTF-8 real en mi base de datos en lugar de estar codificados previamente en HTML porque mi aplicación puede entregar otros formatos como PDF y CSV además de HTML. El lugar apropiado para formatear para HTML es dentro de la plantilla ("ver" si lo desea) que es responsable de representar esa sección de página (fragmento). Quiero convertirlos a sus entidades HTML apropiadas, pero la función get_html_translation_table () de PHP no es súper rápida. Tiene más sentido recuperar los datos una vez y almacenarlos como una matriz, de modo que estén disponibles para que todos los usen. Aquí' Una muestra que toqué para probar la velocidad. Presumiblemente, esto funcionaría independientemente de si los otros métodos que usa (después de obtener la instancia) eran estáticos o no.

class EncodeHTMLEntities {

    private static $instance = null;//stores the instance of self
    private $r = null;//array of chars elligalbe for replacement

    private function __clone(){
    }//disable cloning, no reason to clone

    private function __construct()
    {
        $allEntities = get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES);
        $specialEntities = get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES);
        $this->r = array_diff($allEntities, $specialEntities);
    }

    public static function replace($string)
    {
        if(!(self::$instance instanceof self) ){
            self::$instance = new self();
        }
        return strtr($string, self::$instance->r);
    }
}
//test one million encodings of a string
$start = microtime(true);
for($x=0; $x<1000000; $x++){
    $dump = EncodeHTMLEntities::replace("Reference method for diagnosis of CDAD, but clinical usefulness limited due to extended turnaround time (≥96 hrs)");
}
$end = microtime(true);
echo "Run time: ".($end-$start)." seconds using singleton\n";
//now repeat the same without using singleton
$start = microtime(true);
for($x=0; $x<1000000; $x++){
    $allEntities = get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES);
    $specialEntities = get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES);
    $r = array_diff($allEntities, $specialEntities);
    $dump = strtr("Reference method for diagnosis of CDAD, but clinical usefulness limited due to extended turnaround time (≥96 hrs)", $r);
}
$end = microtime(true);
echo "Run time: ".($end-$start)." seconds without using singleton";

Básicamente, vi resultados típicos como este:

php test.php
Tiempo de ejecución: 27.842966794968 segundos usando singleton
Tiempo de ejecución: 237.78191494942 segundos sin usar singleton

Entonces, aunque ciertamente no soy un experto, no veo una forma más conveniente y confiable de reducir la sobrecarga de las llamadas lentas para algún tipo de datos, mientras lo hago súper simple (una sola línea de código para hacer lo que necesita). De acuerdo, mi ejemplo solo tiene un método útil y, por lo tanto, no es mejor que una función definida globalmente, pero tan pronto como tenga dos métodos, querrá agruparlos, ¿verdad? ¿Estoy fuera de lugar?

Además, prefiero los ejemplos que realmente HACEN algo, ya que a veces es difícil de visualizar cuando un ejemplo incluye declaraciones como "// haz algo útil aquí", que veo todo el tiempo cuando busco tutoriales.

De todos modos, me encantaría cualquier comentario o comentario sobre por qué usar un singleton para este tipo de cosas es perjudicial (o demasiado complicado).

usuario2009125
fuente
1

Este artículo cubre un tema bastante extenso: http://www.phptherightway.com/pages/Design-Patterns.html#singleton

Tenga en cuenta lo siguiente:

  • Se __construct()declara que el constructor protectedimpide crear una nueva instancia fuera de la clase a través del newoperador.
  • El método mágico __clone()se declara privatepara evitar la clonación de una instancia de la clase a través del cloneoperador.
  • El método mágico __wakeup()se declara privatepara evitar la deserialización de una instancia de la clase a través de la función global unserialize().
  • Se crea una nueva instancia a través del enlace estático tardío en el método de creación estática getInstance()con la palabra clave static. Esto permite la subclasificación de class Singletonen el ejemplo.
Krzysztof Przygoda
fuente
1

He escrito hace mucho tiempo pensado compartir aquí

class SingletonDesignPattern {

    //just for demo there will be only one instance
    private static $instanceCount =0;

    //create the private instance variable
    private static $myInstance=null;

    //make constructor private so no one create object using new Keyword
    private function  __construct(){}

    //no one clone the object
    private function  __clone(){}

    //avoid serialazation
    public function __wakeup(){}

    //ony one way to create  object
    public static  function  getInstance(){

        if(self::$myInstance==null){
            self::$myInstance=new SingletonDesignPattern();
            self::$instanceCount++;
        }
        return self::$myInstance;
    }

    public static function getInstanceCount(){
        return self::$instanceCount;
    }

}

//now lets play with singleton design pattern

$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();
$instance = SingletonDesignPattern::getInstance();

echo "number of instances: ".SingletonDesignPattern::getInstanceCount();
Gyaneshwar Pardhi
fuente
0

Estoy de acuerdo con la primera respuesta, pero también declararía la clase como final para que no se pueda extender ya que extender un singleton viola el patrón de singleton. Además, la variable de instancia debe ser privada para que no se pueda acceder directamente. También haga que el método __clone sea privado para que no pueda clonar el objeto singleton.

A continuación se muestra un código de ejemplo.

/**
 * Singleton class
 *
 */
final class UserFactory
{
    private static $_instance = null;

    /**
     * Private constructor
     *
     */
    private function __construct() {}

    /**
     * Private clone method
     *
     */
     private function __clone() {}

    /**
     * Call this method to get singleton
     *
     * @return UserFactory
     */
    public static function getInstance()
    {
        if (self::$_instance === null) {
            self::$_instance = new UserFactory();
        }
        return self::$_instance;
    }
}

Ejemplo de uso

$user_factory = UserFactory::getInstance();

Lo que esto le impide hacer (lo que violaría el patrón singleton ...

¡NO PUEDES HACER ESTO!

$user_factory = UserFactory::$_instance;

class SecondUserFactory extends UserFactory { }
Joseph Crawford
fuente
0

Esta debería ser la forma correcta de Singleton.

class Singleton {

    private static $instance;
    private $count = 0;

    protected function __construct(){

    }

    public static function singleton(){

        if (!isset(self::$instance)) {

            self::$instance = new Singleton;

        }

        return self::$instance;

    }

    public function increment()
    {
        return $this->count++;
    }

    protected function __clone(){

    }

    protected function __wakeup(){

    }

} 
Mário Kapusta
fuente
0

Me gustó el método @ jose-segura de usar rasgos, pero no me gustó la necesidad de definir una variable estática en las subclases. A continuación se muestra una solución que lo evita al almacenar en caché las instancias en una variable local estática del método de fábrica indexado por nombre de clase:

<?php
trait Singleton {

  # Single point of entry for creating a new instance. For a given
  # class always returns the same instance.
  public static function instance(){
    static $instances = array();
    $class = get_called_class();
    if( !isset($instances[$class]) ) $instances[$class] = new $class();
    return $instances[$class];
  }

  # Kill traditional methods of creating new instances
  protected function __clone() {}
  protected function __construct() {}
}

El uso es el mismo que @ jose-segura, solo que no es necesaria la variable estática en las subclases.

Eric Anderson
fuente
0

Clase de base de datos que comprueba si hay alguna instancia de base de datos existente, devolverá la instancia anterior.

   class Database {  
        public static $instance;  
         public static function getInstance(){  
            if(!isset(Database::$instance) ) {  
                Database::$instance = new Database();  
            }  
           return Database::$instance;  
         }  
         private function __cunstruct() {  
           /* private and cant create multiple objects */  
         }  
         public function getQuery(){  
            return "Test Query Data";  
         }  
    }  
    $dbObj = Database::getInstance();  
    $dbObj2 = Database::getInstance();  
    var_dump($dbObj);  
    var_dump($dbObj2);  


/* 
After execution you will get following output: 

object(Database)[1] 
object(Database)[1] 

*/  

Ref. Http://www.phptechi.com/php-singleton-design-patterns-example.html

sunil rajput
fuente
0

Este es el ejemplo de crear singleton en la clase de base de datos

patrones de diseño 1) singleton

class Database{
  public static $instance;
  public static function getInstance(){
    if(!isset(Database::$instance)){
    Database::$instance=new Database();

     return Database::$instance;
    }

  }

  $db=Database::getInstance();
  $db2=Database::getInstance();
  $db3=Database::getInstance();

  var_dump($db);
  var_dump($db2);
  var_dump($db3);

entonces puesto fuera es -

  object(Database)[1]
  object(Database)[1]
  object(Database)[1]

use solo una instancia única, no cree 3 instancias

Surendra Kumar Ahir
fuente
0

Ejemplo rápido:

final class Singleton
{
    private static $instance = null;

    private function __construct(){}

    private function __clone(){}

    private function __wakeup(){}

    public static function get_instance()
    {
        if ( static::$instance === null ) {
            static::$instance = new static();
        }
        return static::$instance;
    }
}

Espero ayuda.

Dmitry
fuente
-4

Este es mi ejemplo que proporciona la capacidad de llamar como $ var = new Singleton () y también crea 3 variables para probar si crea un nuevo objeto:

class Singleton{

    private static $data;

    function __construct(){
        if ($this::$data == null){
            $this->makeSingleton();
        }
        echo "<br/>".$this::$data;
    }

    private function makeSingleton(){
        $this::$data = rand(0, 100);
    }

    public function change($new_val){
        $this::$data = $new_val;
    }

    public function printme(){
        echo "<br/>".$this::$data;
    }

}


$a = new Singleton();
$b = new Singleton();
$c = new Singleton();

$a->change(-2);
$a->printme();
$b->printme();

$d = new Singleton();
$d->printme();
bboydev
fuente
55
Excepto que no es un singleton. Puede crear varias instancias de la clase Singleton.
Andrew Moore
Creo que es después de todo, porque no importa qué instancia afecte a la clase Singleton, los cambios son para todas las instancias de Singleton. He agregado dos funciones más arriba. Ahora, intentemos modificar los datos en una instancia y veamos las otras. Entonces, ¿no es Singleton y si no, qué es incorrecto?
bboydev
55
Un singleton es una clase que permite solo una instancia de sí mismo. Al crear varias instancias, está anulando ese principio.
Andrew Moore