Erstellen des Singleton-Entwurfsmusters in PHP5


204

Wie würde man eine Singleton-Klasse mit PHP5-Klassen erstellen?



1
@ Andrew Instanziieren Sie dann keine zweite Instanz, die eine Verbindung zur Datenbank herstellt. Übergeben Sie diese Instanz an den Ort, an dem sie benötigt wird. Die Notwendigkeit eines Singleton ist ein Code-Geruch. Mehr unter gooh.posterous.com/singletons-in-php
Gordon

3
@ Andrew Mmmmkay. Nichts für ungut, aber ich schlage vor, dass Sie ein Buch über Softwarequalität erhalten, bevor wir diese Diskussion fortsetzen. Singletons vereinfachen nicht, sondern erschweren die normale Wartung und Entwicklung. In der Tat ist es umgekehrt: Es sind Unit-Tests, die die Entwicklung in erster Linie vereinfachen und ermöglichen.
Gordon

3
@ Andrew: Sie gehen jetzt davon aus, dass Sie nur eine Datenbankverbindung benötigen. Was passiert, wenn sich Ihre Anforderungen ändern und Sie tatsächlich mit 2 Datenbankservern sprechen müssen? Ganz zu schweigen davon, wenn Sie Ihrem Team nicht vertrauen können, dass es die Dinge richtig macht , hilft Ihnen das Erstellen eines Singletons nicht im geringsten. Machen Sie die Dinge von Anfang an richtig und schaffen Sie ein Team, dem Sie vertrauen können und dem es gut geht.
Ircmaxell

4
Nur weil der Singleton überbeansprucht wurde, ist er kein schlechtes Muster, das vermieden werden sollte. Hassen Sie den Singleton nicht. Manchmal ist es eine perfekte Lösung für ein bestimmtes Problem. Fangen Sie besser an zu argumentieren, warum wir es nicht verwenden sollten, anstatt nur emotional zu versuchen, es zu verschlechtern.
Gilles Lesire

Antworten:


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

    }
}

Benutzen:

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

$fact == $fact2;

Aber:

$fact = new UserFactory()

Wirft einen Fehler.

Unter http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static finden Sie Informationen zu statischen Variablenbereichen und warum die Einstellung static $inst = null;funktioniert.


59
Um die beiden Instanzen zu vergleichen, sollten Sie === anstelle von == verwenden. == gibt true zurück, wenn $ fact1 und $ fact2 beide zur selben Klasse gehören, === gibt jedoch nur true zurück, wenn beide dieselbe Instanz desselben Objekts sind.
Keith Twombley

10
Klon-Methode sollte auch privat sein
Alex Petrov

22
Wird diese Methode die Instanz von UserFactory nicht bei jedem Aufruf von Instance () auf null zurücksetzen? In Java wäre die Variable $ inst ein privates statisches Attribut, das nicht immer wieder zurückgesetzt werden sollte, da Sie es sonst genauso gut nicht zu einem Singleton machen könnten.
Rudy Garcia

8
Hier ist eine gute Beschreibung, warum und wie das Deklarieren der Variablen als statisch in der Funktion funktioniert, wie es der Autor beabsichtigt: php.net/manual/en/…
hereswhatidid

10
Sie sollten $ inst = new self () verwenden; nicht $ inst = new UserFactory (); für alle, die später darauf stoßen. +1 für die Verwendung einer integrierten PHP-Methodik.
Ligemer

119

PHP 5.3 ermöglicht die Erstellung einer vererbbaren Singleton-Klasse durch späte statische Bindung:

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

Dies löst das Problem, dass vor PHP 5.3 jede Klasse, die einen Singleton erweitert hat, eine Instanz ihrer übergeordneten Klasse anstelle ihrer eigenen erzeugt.

Jetzt können Sie tun:

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

Und $ foo wird eine Instanz von Foobar anstelle einer Instanz von Singleton sein.


1
Eine späte statische Bindung ist in PHP 5.3 in der Tat eine sehr gute Sache. Schade, dass ich es immer noch nicht benutzen kann.
AntonioCS

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

4
Das funktioniert überhaupt nicht, es kommt nur so vor, dass Foobar die erste Klasse war, die Sie gebaut haben?
Chris KL

1
noch eine Möglichkeit zum Klonen ..... "$ a = Singleton :: getInstance (); $ b = unserialize (serialize ($ a)); $ a! == $ b;"
Bortunac

15
Dies funktioniert nicht, wenn es mehr als eine Unterklasse gibt! $instancebefindet sich in Singleton, nicht in der Unterklasse. Nachdem eine Unterklasse instanziiert wurde, gibt getInstance () diese Instanz für alle Unterklassen zurück.
Mpartel

116

Leider bricht die Antwort von Inwdr ab, wenn mehrere Unterklassen vorhanden sind.

Hier ist eine korrekte vererbbare Singleton-Basisklasse.

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

Testcode:

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

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

1
Dies kommt der korrekten Singleton-Implementierung bisher am nächsten. Sie sollten auch in Betracht ziehen, die Methode __wakeup () zu verwenden, um eine Unserialisierung zu verhindern.
Robert Rossmann

Tatsächlich müssen Sie entweder eine Ausnahme auslösen oder einen Fehler manuell auslösen. Wenn Sie die Funktion als geschützt / privat deklarieren, wird nur eine E_WARNING ausgelöst, die besagt, dass sie nicht auf die Methode zugreifen kann, aber ansonsten fortfahren würde.
Robert Rossmann

Vielen Dank. Normalerweise habe ich alle Warnungen usw. in Ausnahmen umgewandelt, daher habe ich den Unterschied beim
Testen

Dies ist die einzige Lösung, die ich gefunden habe und die richtig mit mehreren Unterklassen umgeht. Danke dir!
Bob Dankert

36

Die echte und moderne Art, Singleton-Muster zu erstellen, ist:

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

}

So, jetzt können Sie es wie verwenden.

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

Wie Sie sehen, ist diese Erkenntnis viel flexibler.


4
Dies ist die klarste Antwort zum Singleton-Muster in diesem Thread. Vielen Dank.
Gus

Ich habe diesen Ansatz implementiert und er funktioniert wie erwartet: Die zweite Instanz wird null. Allerdings musste ich auch die Betonklasse nicht erweitern. Ich habe gerade die Singleton :: instance () im Konstruktor dieser konkreten Klasse implementiert.
Snaphuman

in der instanceFunktion $instancesollte nullnicht seinfalse
Mifas

Ja, aber es ist keine Funktion, sondern eine Methode.
Abraham Tugalov

26

Sie sollten wahrscheinlich eine private __clone () -Methode hinzufügen, um das Klonen einer Instanz zu verbieten.

private function __clone() {}

Wenn Sie diese Methode nicht einschließen, ist Folgendes möglich

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

jetzt $inst1! == $inst2- sie sind nicht mehr dieselbe Instanz.


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

benutzen:

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

Antwort:

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

Wenn Sie PHP 5.4 verwenden: Trait ist eine Option, sodass Sie die Vererbungshierarchie nicht verschwenden müssen, um das Singleton-Muster zu erhalten

Beachten Sie auch, dass unabhängig davon, ob Sie Merkmale verwenden oder die Singleton- Klasse erweitern, ein loses Ende darin bestand, ein Singleton untergeordneter Klassen zu erstellen, wenn Sie nicht die folgende Codezeile hinzufügen:

   protected static $inst = null;

in der Kinderklasse

Das unerwartete Ergebnis wird sein:

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

Dieser Code kann für jede Klasse gelten, ohne sich um ihren Klassennamen zu kümmern.


8

Unterstützt mehrere Objekte mit 1 Zeile pro Klasse:

Diese Methode erzwingt Singletons für jede gewünschte Klasse. Sie müssen lediglich 1 Methode zu der Klasse hinzufügen, für die Sie einen Singleton erstellen möchten, und dies erledigt dies für Sie.

Dadurch werden auch Objekte in einer "SingleTonBase" -Klasse gespeichert, sodass Sie alle Ihre in Ihrem System verwendeten SingleTonBaseObjekte durch Rekursion der Objekte debuggen können.


Erstellen Sie eine Datei namens SingletonBase.php und fügen Sie sie in das Stammverzeichnis Ihres Skripts ein!

Der Code ist

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

Fügen Sie dann für jede Klasse, die Sie zu einem Singleton machen möchten, einfach diese kleine Einzelmethode hinzu.

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

Hier ist ein kleines Beispiel:

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

Sie können diese Singleton-Funktion einfach in jede Klasse einfügen und es wird nur 1 Instanz pro Klasse erstellt.

HINWEIS: Sie sollten das __construct immer privat machen, um die Verwendung der neuen Klasse () zu vermeiden. Instanziierungen.


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

Sie müssen das Singleton-Muster nicht wirklich verwenden, da es als Antimuster betrachtet wird. Grundsätzlich gibt es viele Gründe, dieses Muster überhaupt nicht zu implementieren. Lesen Sie dies zunächst: Best Practice für PHP-Singleton-Klassen .

Wenn Sie immer noch der Meinung sind, dass Sie das Singleton-Muster verwenden müssen, können wir eine Klasse schreiben, mit der wir die Singleton-Funktionalität erhalten, indem wir unsere abstrakte SingletonClassVendor-Klasse erweitern.

Dies ist, was ich kam, um dieses Problem zu lösen.

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

}

Anwendungsbeispiel:

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

Nur um zu beweisen, dass es wie erwartet funktioniert:

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

Während ich mehr positive Antworten las, dachte ich an so etwas. Zum Glück war es schon da :)
hatef

3

All diese Komplexität ("Late Static Binding" ... Harumph) ist für mich einfach ein Zeichen für das kaputte Objekt- / Klassenmodell von PHP. Wenn Klassenobjekte erstklassige Objekte waren (siehe Python), dann „$ _instance“ wäre eine Klasse Instanz Variable - ein Mitglied des Klassenobjekts, im Gegensatz zu einem Mitglied / Eigentum seiner Instanzen und auch im Gegensatz zu gemeinsamen von seinen Nachkommen. In der Smalltalk-Welt ist dies der Unterschied zwischen einer "Klassenvariablen" und einer "Klasseninstanzvariablen".

In PHP sieht es für mich so aus, als müssten wir uns die Anleitung zu Herzen nehmen, dass Muster eine Anleitung zum Schreiben von Code sind - wir denken vielleicht an eine Singleton-Vorlage, versuchen aber, Code zu schreiben, der von einer tatsächlichen "Singleton" -Klasse erbt sieht für PHP falsch aus (obwohl ich vermutete, dass eine unternehmungslustige Seele ein geeignetes SVN-Schlüsselwort erstellen könnte).

Ich werde weiterhin nur jeden Singleton einzeln mit einer gemeinsam genutzten Vorlage codieren.

Beachten Sie, dass ich mich absolut aus der Singletons-Are-Evil-Diskussion heraushalte. Das Leben ist zu kurz.


Ihre Bemerkungen sind richtig, wenn Sie die ständig zunehmende Komplexität der PHP-Sprache betrachten. Es scheint, als würden zu viele neue Schlüsselwörter hinzugefügt, um aus zu vielen verschiedenen Designlücken in zu vielen verschiedenen Codierungsparadigmen herauszukommen. Schlimmer noch, aufgrund der hohen Änderungsrate und des Versionsversatzes zwischen Hosts und Entwicklungsplattformen funktioniert die heutige "Solution du Jour" (wie Merkmale in der Antwort von @Eric Anderson [ stackoverflow.com/a/23998306/3696363] ) nicht auf Produktionssystemen, auf denen möglicherweise eine "stabile" Version anstelle der "neuesten, besten" ausgeführt wird.
Eliyahu Skoczylas

2

Ich weiß, dass dies wahrscheinlich zu einem unnötigen Flammenkrieg führen wird, aber ich kann sehen, dass Sie möglicherweise mehr als eine Datenbankverbindung wünschen, daher würde ich zugeben, dass Singleton möglicherweise nicht die beste Lösung dafür ist ... es gibt jedoch solche andere Verwendungen des Singleton-Musters, die ich äußerst nützlich finde.

Hier ein Beispiel: Ich habe mich entschieden, meine eigene MVC- und Template-Engine zu rollen, weil ich etwas wirklich Leichtes wollte. Die Daten, die ich anzeigen möchte, enthalten jedoch viele spezielle mathematische Zeichen wie ≥ und μ und was haben Sie ... Die Daten werden als das eigentliche UTF-8-Zeichen in meiner Datenbank gespeichert und nicht vor HTML codiert, weil Meine App kann neben HTML auch andere Formate wie PDF und CSV liefern. Der geeignete Ort zum Formatieren von HTML befindet sich in der Vorlage ("Ansicht", wenn Sie so wollen), die für das Rendern dieses Seitenabschnitts (Snippet) verantwortlich ist. Ich möchte sie in die entsprechenden HTML-Entitäten konvertieren, aber die Funktion get_html_translation_table () der PHP ist nicht sehr schnell. Es ist sinnvoller, die Daten einmal abzurufen und als Array zu speichern, damit sie allen zur Verfügung stehen. Hier' Als Probe habe ich zusammen geklopft, um die Geschwindigkeit zu testen. Vermutlich würde dies unabhängig davon funktionieren, ob die anderen von Ihnen verwendeten Methoden (nach dem Abrufen der Instanz) statisch waren oder nicht.

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

Grundsätzlich habe ich typische Ergebnisse wie diese gesehen:

php test.php
Laufzeit: 27.842966794968 Sekunden mit Singleton
Laufzeit: 237.78191494942 Sekunden ohne Verwendung von Singleton

Obwohl ich sicherlich kein Experte bin, sehe ich keinen bequemeren und zuverlässigeren Weg, um den Overhead langsamer Anrufe für irgendeine Art von Daten zu reduzieren und es gleichzeitig super einfach zu machen (einzelne Codezeile, um das zu tun, was Sie brauchen). Zugegeben, mein Beispiel hat nur eine nützliche Methode und ist daher nicht besser als eine global definierte Funktion, aber sobald Sie zwei Methoden haben, werden Sie sie zusammen gruppieren wollen, oder? Bin ich weit weg von der Basis?

Außerdem bevorzuge ich Beispiele, die tatsächlich etwas tun, da es manchmal schwer zu visualisieren ist, wenn ein Beispiel Anweisungen wie "// hier etwas Nützliches tun" enthält, die ich bei der Suche nach Tutorials ständig sehe.

Wie auch immer, ich würde mich über Feedback oder Kommentare freuen, warum die Verwendung eines Singletons für diese Art von Dingen schädlich (oder übermäßig kompliziert) ist.


1

Dieser Artikel behandelt das Thema ziemlich ausführlich: http://www.phptherightway.com/pages/Design-Patterns.html#singleton

Beachte das Folgende:

  • Der Konstruktor __construct()soll deklarieren protected, dass über den newOperator keine neue Instanz außerhalb der Klasse erstellt werden soll .
  • Die magische Methode __clone()soll deklarieren private, um das Klonen einer Instanz der Klasse über den cloneOperator zu verhindern.
  • Die magische Methode __wakeup()soll deklarieren private, um die Unserialisierung einer Instanz der Klasse über die globale Funktion zu verhindern unserialize().
  • Eine neue Instanz wird über eine späte statische Bindung in der statischen Erstellungsmethode getInstance()mit dem Schlüsselwort erstellt static. Dies ermöglicht die Unterklasse der class Singletonim Beispiel.

1

Ich habe lange zurückgedacht, hier zu teilen

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

Ich stimme der ersten Antwort zu, würde aber auch die Klasse als endgültig deklarieren, damit sie nicht erweitert werden kann, da das Erweitern eines Singletons das Singleton-Muster verletzt. Außerdem sollte die Instanzvariable privat sein, damit nicht direkt auf sie zugegriffen werden kann. Machen Sie die __clone-Methode auch privat, damit Sie das Singleton-Objekt nicht klonen können.

Unten finden Sie einen Beispielcode.

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

Beispiel Verwendung

$user_factory = UserFactory::getInstance();

Was dies Sie davon abhält (was das Singleton-Muster verletzen würde ..

DU KANNST DAS NICHT MACHEN!

$user_factory = UserFactory::$_instance;

class SecondUserFactory extends UserFactory { }

0

Dies sollte der richtige Weg von Singleton sein.

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

Ich mochte die @ jose-segura-Methode zur Verwendung von Merkmalen, mochte aber nicht die Notwendigkeit, eine statische Variable für Unterklassen zu definieren. Im Folgenden finden Sie eine Lösung, die dies vermeidet, indem die Instanzen in einer statischen lokalen Variablen an die Factory-Methode zwischengespeichert werden, die durch den Klassennamen indiziert ist:

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

Die Verwendung ist die gleiche wie bei @ jose-segura, nur dass die statische Variable in Unterklassen nicht benötigt wird.


0

Eine Datenbankklasse, die prüft, ob eine Datenbankinstanz vorhanden ist, gibt die vorherige Instanz zurück.

   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] 

*/  

Siehe http://www.phptechi.com/php-singleton-design-patterns-example.html


0

Dies ist das Beispiel für das Erstellen eines Singletons für die Datenbankklasse

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

dann wird ausgegeben -

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

Verwenden Sie nur eine einzelne Instanz und erstellen Sie keine 3 Instanzen


0

Kurzes Beispiel:

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

Hoffe Hilfe.


-4

Hier ist mein Beispiel, das die Möglichkeit bietet, als $ var = new Singleton () aufzurufen und außerdem 3 Variablen zu erstellen, um zu testen, ob ein neues Objekt erstellt wird:

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
Nur dass es kein Singleton ist. Sie können mehrere Instanzen der Klasse Singleton erstellen.
Andrew Moore

Ich denke, es ist immerhin so, denn unabhängig davon, welche Instanz sich auf die Singleton-Klasse auswirkt, gelten Änderungen für alle Instanzen von Singleton. Ich habe oben zwei weitere Funktionen hinzugefügt. Versuchen wir nun, die Daten in einer Instanz zu ändern und die anderen zu überprüfen. Also, ist es nicht Singleton und wenn nein - was ist falsch?
Bboydev

5
Ein Singleton ist eine Klasse, die nur eine Instanz von sich selbst zulässt. Durch das Erstellen mehrerer Instanzen wird dieses Prinzip ungültig.
Andrew Moore
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.