Création du modèle de conception Singleton en PHP5


204

Comment créer une classe Singleton en utilisant des classes PHP5?



1
@Andrew N'instanciez alors pas une seconde instance se connectant à la base de données. Passez cette instance là où elle est nécessaire. Le besoin d'un Singleton est une odeur de code. Plus sur gooh.posterous.com/singletons-in-php
Gordon

3
@Andrew Mmmmkay. Aucune infraction, mais je vous suggère de vous procurer un livre sur la qualité des logiciels avant de poursuivre cette discussion. Les singletons ne simplifient pas mais compliquent la maintenance et le développement normaux. En fait, c'est l'inverse: ce sont les tests unitaires qui simplifient et permettent le développement en premier lieu.
Gordon

3
@Andrew: Vous supposez maintenant que vous n'avez besoin que d'une seule connexion à la base de données. Que se passe-t-il lorsque vos besoins changent et que vous devez réellement parler à 2 serveurs de base de données? Sans parler si vous ne pouvez pas faire confiance à votre équipe pour faire les choses à droite , la création d' un singleton ne vous aidera pas dans le moins. Faites les choses dès le début et obtenez une équipe en qui vous pouvez avoir confiance et tout ira bien.
ircmaxell

4
Ce n'est pas parce que le Singleton a été surutilisé qu'il s'agit d'un mauvais schéma à éviter. Ne déteste pas le Singleton. Parfois, c'est une solution parfaitement bonne à un certain problème. Mieux vaut commencer à argumenter pourquoi nous ne devrions pas l'utiliser au lieu de simplement essayer émotionnellement de le dégrader.
Gilles Lesire

Réponses:


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

    }
}

Utiliser:

$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();

$fact == $fact2;

Mais:

$fact = new UserFactory()

Lance une erreur.

Voir http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static pour comprendre les portées des variables statiques et pourquoi le réglage static $inst = null;fonctionne.


59
pour comparer les deux instances, vous devez utiliser === plutôt que ==. == renverra true si $ fact1 et $ fact2 sont tous deux de la même classe, mais === ne renvoie true que s'ils sont tous les deux la même instance du même objet.
Keith Twombley

10
la méthode de clonage devrait également être privée
Alex Petrov

22
Cette méthode ne réinitialisera-t-elle pas l'instance de UserFactory à null chaque fois que vous appelez Instance ()? En java, la variable $ inst serait un attribut statique privé qui ne devrait pas être réinitialisé encore et encore, sinon vous pourriez aussi bien ne pas en faire un singleton.
Rudy Garcia

8
Voici une bonne description de pourquoi et comment déclarer la variable comme statique dans la fonction fonctionne comme l'auteur le souhaite
hereswhatidid

10
Vous devriez utiliser $ inst = new self (); pas $ inst = new UserFactory (); pour toute personne rencontrant cela plus tard. +1 pour l'utilisation d'une méthodologie PHP intégrée.
Ligemer

119

PHP 5.3 permet la création d'une classe Singleton héritable via une liaison statique tardive:

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;
    }
}

Cela résout le problème qu'avant PHP 5.3, toute classe qui étendait un Singleton produirait une instance de sa classe parente au lieu de la sienne.

Vous pouvez maintenant:

class Foobar extends Singleton {};
$foo = Foobar::getInstance();

Et $ foo sera une instance de Foobar au lieu d'une instance de Singleton.


1
La liaison statique tardive est en effet une très bonne chose dans php 5.3. Dommage que je ne puisse toujours pas l'utiliser.
AntonioCS

4
De @ggsonic: "subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());".
Brock Adams

4
Cela ne fonctionne pas du tout, il se trouve que Foobar était la première classe que vous avez construite?
Chris KL

1
encore une possibilité de cloner ..... "$ a = Singleton :: getInstance (); $ b = unserialize (serialize ($ a)); $ a! == $ b;"
bortunac

15
Cela ne fonctionne pas lorsqu'il existe plusieurs sous-classes! $instanceréside à Singleton, pas la sous-classe. Après l'instanciation d'une sous-classe, getInstance () renverra cette instance pour toutes les sous-classes.
mpartel

116

Malheureusement, la réponse d'Inwdr se casse lorsqu'il y a plusieurs sous-classes.

Voici une classe de base Singleton héritable correcte.

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];
    }
}

Code de test:

class Foo extends Singleton {}
class Bar extends Singleton {}

echo get_class(Foo::getInstance()) . "\n";
echo get_class(Bar::getInstance()) . "\n";

1
C'est jusqu'à présent le plus proche de corriger l'implémentation Singleton. Vous devriez également envisager de lancer la méthode __wakeup () pour éviter la désérialisation.
Robert Rossmann

En fait, vous devez lever une exception ou déclencher une erreur manuellement - déclarer la fonction protégée / privée ne fera que déclencher un E_WARNING disant qu'elle ne peut pas accéder à la méthode, mais continuerait autrement.
Robert Rossmann

Merci. Normalement, tous les avertissements, etc. sont transformés en exceptions, j'ai donc oublié la différence lorsque j'ai testé: P
mpartel

C'est la seule solution que j'ai trouvée qui traite correctement plusieurs sous-classes. Je vous remercie!
Bob Dankert

36

La façon réelle et moderne de créer un motif Singleton est la suivante:

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

}

Alors maintenant, vous pouvez l'utiliser comme.

<?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

Comme vous le voyez, cette réalisation est beaucoup plus flexible.


4
C'est la réponse la plus claire sur le modèle Singleton dans ce fil. Merci.
Gus

J'ai implémenté cette approche et cela fonctionne comme prévu: la deuxième instance devient nulle. Cependant, je n'avais pas non plus besoin d'étendre la classe concrète. Je viens d'implémenter Singleton :: instance () dans le constructeur de cette classe concrète.
snaphuman

dans la instancefonction $instancene devrait nullpas êtrefalse
Mifas

Oui, mais ce n'est pas une fonction, mais une méthode.
Abraham Tugalov

26

Vous devriez probablement ajouter une méthode privée __clone () pour interdire le clonage d'une instance.

private function __clone() {}

Si vous n'incluez pas cette méthode, ce qui suit devient possible

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

maintenant $inst1! == $inst2- ce ne sont plus la même instance.


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

utiliser:

/**
 *  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;

réponse:

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

Si vous utilisez PHP 5.4: trait c'est une option, vous n'avez donc pas à gaspiller la hiérarchie d'héritage pour avoir le modèle Singleton

et notez également que si vous utilisez des traits ou étend la classe Singleton une extrémité libre était de créer un singleton de classes enfants si vous n'ajoutez pas la ligne de code suivante:

   protected static $inst = null;

en classe enfant

le résultat inattendu sera:

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

10
protected  static $_instance;

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

Ce code peut s'appliquer à n'importe quelle classe sans se soucier de son nom de classe.


8

Prend en charge plusieurs objets avec 1 ligne par classe:

Cette méthode imposera des singletons sur n'importe quelle classe que vous souhaitez, tout ce que vous devez faire est d'ajouter 1 méthode à la classe que vous souhaitez faire un singleton et cela le fera pour vous.

Cela stocke également les objets dans une classe "SingleTonBase" afin que vous puissiez déboguer tous vos objets que vous avez utilisés dans votre système en recursant les SingleTonBaseobjets.


Créez un fichier appelé SingletonBase.php et incluez-le à la racine de votre script!

Le code est

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;
    }
}

Ensuite, pour n'importe quelle classe que vous souhaitez créer un singleton, ajoutez simplement cette petite méthode unique.

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

Voici un petit exemple:

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();

Et vous pouvez simplement ajouter cette fonction singleton dans n'importe quelle classe que vous avez et elle ne créera qu'une seule instance par classe.

REMARQUE: vous devez toujours rendre le __construct privé pour éliminer l'utilisation de new Class (); instanciations.


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();

5

Vous n'avez pas vraiment besoin d'utiliser le modèle Singleton car il est considéré comme un contre-modèle. Fondamentalement, il existe de nombreuses raisons de ne pas du tout mettre en œuvre ce modèle. Lisez ceci pour commencer: Meilleures pratiques sur les classes PHP singleton .

Si après tout vous pensez toujours que vous devez utiliser le modèle Singleton, nous pourrions écrire une classe qui nous permettra d'obtenir la fonctionnalité Singleton en étendant notre classe abstraite SingletonClassVendor.

C'est ce que je suis venu pour résoudre ce problème.

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

}

Exemple d'utilisation:

/**
 * 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
    }
}

Juste pour prouver que cela fonctionne comme prévu:

/**
 *  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

En lisant des réponses plus votées, j'avais quelque chose comme ça à l'esprit. Heureusement, il était déjà là :)
hatef

3

Toute cette complexité ("liaison statique tardive" ... harumph) est, pour moi, simplement un signe du modèle objet / classe cassé de PHP. Si les objets de classe étaient des objets de première classe (voir Python), alors "$ _instance" serait une instance de classe variable d' - un membre de l'objet de classe, par opposition à un membre / propriété de ses instances, et également par opposition à partagé par ses descendants. Dans le monde Smalltalk, c'est la différence entre une "variable de classe" et une "variable d'instance de classe".

En PHP, il me semble que nous devons prendre à cœur les indications selon lesquelles les modèles sont un guide pour écrire du code - nous pourrions peut-être penser à un modèle Singleton, mais essayer d'écrire du code qui hérite d'une classe "Singleton" réelle semble mal orienté pour PHP (même si je supposais qu'une âme entreprenante pourrait créer un mot clé SVN approprié).

Je vais continuer à coder chaque singleton séparément, en utilisant un modèle partagé.

Remarquez que je reste absolument HORS de la discussion singletons-are-evil, la vie est trop courte.


Vos remarques sont justes lorsque vous visualisez la complexité toujours croissante du langage PHP. Il semble que trop de nouveaux mots-clés soient ajoutés pour sortir de trop nombreux trous de conception différents dans trop de paradigmes de codage différents. Pire, en raison du taux de changement élevé et du biais de version entre les hôtes et les plates-formes de développement, la "solution du jour" d'aujourd'hui (comme les traits de la réponse de @Eric Anderson [ stackoverflow.com/a/23998306/3696363] ), ne fonctionne pas sur les systèmes de production qui pourraient exécuter une version "stable" au lieu de la "dernière, la meilleure".
Eliyahu Skoczylas

2

Je sais que cela va probablement provoquer une guerre des flammes inutile, mais je peux voir comment vous pourriez vouloir plus d'une connexion à la base de données, donc je concéderais que singleton pourrait ne pas être la meilleure solution pour cela ... cependant, il y a d'autres utilisations du motif singleton que je trouve extrêmement utiles.

Voici un exemple: j'ai décidé de lancer mon propre MVC et moteur de template parce que je voulais quelque chose de vraiment léger. Cependant, les données que je veux afficher contiennent beaucoup de caractères mathématiques spéciaux tels que ≥ et μ et qu'avez-vous ... Les données sont stockées en tant que caractère UTF-8 réel dans ma base de données plutôt que pré-HTML car mon application peut fournir d'autres formats tels que PDF et CSV en plus du HTML. L'endroit approprié pour formater le code HTML est à l'intérieur du modèle ("afficher" si vous voulez) qui est responsable du rendu de cette section de la page (extrait). Je veux les convertir en leurs entités HTML appropriées, mais la fonction get_html_translation_table () des PHP n'est pas super rapide. Il est plus judicieux de récupérer les données une fois et de les stocker sous forme de tableau, ce qui les rend accessibles à tous. Ici' Dans un échantillon, j'ai frappé ensemble pour tester la vitesse. Vraisemblablement, cela fonctionnerait indépendamment du fait que les autres méthodes que vous utilisez (après avoir obtenu l'instance) soient statiques ou non.

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";

Fondamentalement, j'ai vu des résultats typiques comme celui-ci:

php test.php
Durée: 27,842966794968 secondes en utilisant singleton
Durée: 237,78191494942 secondes sans utiliser singleton

Donc, même si je ne suis certainement pas un expert, je ne vois pas de moyen plus pratique et fiable de réduire la surcharge des appels lents pour une sorte de données, tout en le rendant super simple (une seule ligne de code pour faire ce dont vous avez besoin). Certes, mon exemple n'a qu'une seule méthode utile, et n'est donc pas mieux qu'une fonction définie globalement, mais dès que vous avez deux méthodes, vous allez vouloir les regrouper, non? Suis-je loin de la base?

De plus, je préfère les exemples qui font réellement quelque chose, car il est parfois difficile de visualiser quand un exemple comprend des instructions comme "// faire quelque chose d'utile ici" que je vois tout le temps lors de la recherche de tutoriels.

Quoi qu'il en soit, j'aimerais avoir des commentaires ou des commentaires sur les raisons pour lesquelles l'utilisation d'un singleton pour ce type de chose est préjudiciable (ou trop compliquée).


1

Cet article couvre un sujet assez largement: http://www.phptherightway.com/pages/Design-Patterns.html#singleton

Notez les points suivants:

  • Le constructeur __construct()est déclaré comme protectedempêchant la création d'une nouvelle instance en dehors de la classe via l' newopérateur.
  • La méthode magique __clone()est déclarée de manière privateà empêcher le clonage d'une instance de la classe via l' cloneopérateur.
  • La méthode magique __wakeup()est déclarée privatepour empêcher la désérialisation d'une instance de la classe via la fonction globale unserialize().
  • Une nouvelle instance est créée via une liaison statique tardive dans la méthode de création statique getInstance()avec le mot clé static. Cela permet de sous-classer le class Singletondans l'exemple.

1

J'ai écrit depuis longtemps pensé à partager ici

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();

0

Je suis d'accord avec la première réponse mais je déclarerais également la classe comme finale afin qu'elle ne puisse pas être étendue car l'extension d'un singleton viole le modèle singleton. La variable d'instance doit également être privée afin de ne pas être accessible directement. Rendez également la méthode __clone privée afin que vous ne puissiez pas cloner l'objet singleton.

Voici un exemple de code.

/**
 * 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;
    }
}

Exemple d'utilisation

$user_factory = UserFactory::getInstance();

Ce que cela vous empêche de faire (ce qui violerait le modèle singleton ..

TU NE PEUX PAS FAIRE ÇA!

$user_factory = UserFactory::$_instance;

class SecondUserFactory extends UserFactory { }

0

Cela devrait être la bonne façon 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(){

    }

} 

0

J'ai aimé la méthode @ jose-segura d'utiliser des traits mais je n'ai pas aimé la nécessité de définir une variable statique sur les sous-classes. Voici une solution qui l'évite en mettant en cache les instances d'une variable locale statique dans la méthode d'usine indexée par nom de classe:

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

L'utilisation est la même que @ jose-segura mais pas besoin de la variable statique dans les sous-classes.


0

Classe de base de données qui vérifie s'il existe une instance de base de données existante, elle retournera l'instance précédente.

   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] 

*/  

Réf http://www.phptechi.com/php-singleton-design-patterns-example.html


0

Ceci est l'exemple de création de singleton sur la classe Database

motifs de conception 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);

alors mis est -

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

utiliser une seule instance et non pas créer 3 instances


0

Exemple rapide:

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;
    }
}

J'espère de l'aide.


-4

Voici mon exemple qui offre la possibilité d'appeler en tant que $ var = new Singleton () et de créer également 3 variables pour tester s'il crée un nouvel objet:

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();

5
Sauf que ce n'est pas un singleton. Vous pouvez créer plusieurs instances de la classe Singleton.
Andrew Moore

Je pense que c'est après tout, car quelle que soit l'instance affectant la classe Singleton, les modifications concernent toutes les instances de Singleton. J'ai ajouté deux autres fonctions ci-dessus. Maintenant, essayons de modifier les données dans une instance et vérifions les autres. Alors, n'est-ce pas Singleton et si non - qu'est-ce qui est incorrect?
bboydev

5
Un singleton est une classe qui n'autorise qu'une seule instance d'elle-même. En créant plusieurs instances, vous annulez ce principe.
Andrew Moore
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.