Wie würde man eine Singleton-Klasse mit PHP5-Klassen erstellen?
Erstellen des Singleton-Entwurfsmusters in PHP5
Andreas Moore
/**
* 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.
Sehen http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static um den Gültigkeitsbereich statischer Variablen und die Gründe für die Einstellung zu verstehen static $inst = null;
funktioniert.
-
Um die beiden Instanzen zu vergleichen, sollten Sie === anstelle von == verwenden. == gibt wahr zurück, wenn $fakt1 und $fakt2 beide derselben Klasse angehören, aber === gibt nur wahr zurück, wenn sie beide dieselbe Instanz desselben Objekts sind.
– Keith Twombley
15. Oktober 2008 um 1:02 Uhr
-
Die Klonmethode sollte auch privat sein
– Alex Petrow
10. April 2013 um 4:22 Uhr
-
Wird diese Methode die Instanz von UserFactory nicht jedes Mal auf null zurücksetzen, wenn Sie Instance() aufrufen? In Java wäre die $inst-Variable ein privates statisches Attribut, das nicht immer wieder zurückgesetzt werden sollte, sonst könnten Sie es genauso gut nicht zu einem Singleton machen.
– Rudy García
7. Juni 2013 um 14:55 Uhr
-
Hier ist eine gute Beschreibung, warum und wie das Deklarieren der Variablen als statisch in der Funktion so funktioniert, wie der Autor beabsichtigt: php.net/manual/en/…
– Hereswhatidid
24. Januar 2015 um 5:48 Uhr
-
Sie sollten $inst = new self(); nicht $inst = new UserFactory(); für alle, die später darauf stoßen. +1 für die Verwendung einer integrierten PHP-Methodik.
– Liger
18. Februar 2015 um 3:27 Uhr
Teil
Leider bricht die Antwort von Inwdr, wenn es mehrere Unterklassen gibt.
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";
-
Dies kommt der korrekten Singleton-Implementierung bisher am nächsten. Auch das Einwerfen sollte in Erwägung gezogen werden __Wach auf() Methode zur Vermeidung von Deserialisierung.
– Robert Roßmann
27. Oktober 2013 um 21:25 Uhr
-
Tatsächlich müssen Sie entweder eine Ausnahme auslösen oder manuell einen Fehler 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 andernfalls fortfahren würde.
– Robert Roßmann
28. Oktober 2013 um 21:29 Uhr
-
Danke. Ich habe normalerweise alle Warnungen etc. in Ausnahmen umgewandelt, also habe ich den Unterschied vergessen, als ich getestet habe 😛
– Teil
28. Oktober 2013 um 22:43 Uhr
-
Dies ist die einzige Lösung, die ich gefunden habe, die mehrere Unterklassen richtig behandelt. Danke!
– Bob Dankert
22. Mai 2019 um 16:16 Uhr
selbstbewusste Suppe
PHP 5.3 ermöglicht die Erstellung einer vererbbaren Singleton-Klasse über spätes statisches Binden:
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 ein Singleton erweitert hat, statt ihrer eigenen eine Instanz ihrer übergeordneten Klasse erzeugt hat.
Jetzt können Sie Folgendes tun:
class Foobar extends Singleton {};
$foo = Foobar::getInstance();
Und $foo wird eine Instanz von Foobar statt einer Instanz von Singleton sein.
-
Spätes statisches Binden ist in der Tat eine sehr gute Sache in PHP 5.3. Schade, dass ich es immer noch nicht verwenden kann.
– AntonioCS
21. Dezember 2009 um 10:52 Uhr
-
Von @ggsonic:
"subclass should own its own static var. check this: echo get_class(Foobar::getInstance());echo get_class(Singleton::getInstance());"
.– Brock Adams
1. Dezember 2011 um 5:39 Uhr
-
Das funktioniert überhaupt nicht, es ist einfach so, dass Foobar die erste Klasse war, die Sie gebaut haben?
– Chris KL
10. September 2012 um 6:40 Uhr
-
noch eine Klonmöglichkeit ….. “$a=Singleton::getInstance(); $b=unserialize(serialize($a)); $a!==$b;”
– Bortunac
15. Oktober 2012 um 5:05 Uhr
-
Dies funktioniert nicht, wenn es mehr als eine Unterklasse gibt!
$instance
befindet sich in Singleton, nicht in der Unterklasse. Nachdem eine Unterklasse instanziiert wurde, gibt getInstance() diese Instanz für alle Unterklassen zurück.– Teil
8. April 2013 um 2:22 Uhr
Abraham Tugalow
Das Echte und Moderne Möglichkeit, 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 gerne 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 Realisierung viel flexibler.
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, wird 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.
<?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: Merkmal Es ist eine Option, sodass Sie die Vererbungshierarchie nicht verschwenden müssen, um die zu haben Singleton-Muster
und beachten Sie auch, ob Sie verwenden Züge oder erweitert Singleton Das lose Ende von Klasse 1 bestand darin, Singleton von untergeordneten Klassen zu erstellen, wenn Sie die folgende Codezeile nicht hinzufügen:
protected static $inst = null;
in der Kinderklasse
das unerwartete Ergebnis wird sein:
object(DBFactoryChild)#1 (0) {
}
object(DBFactoryChild)#1 (0) {
}
hungneox
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 den Klassennamen zu kümmern.
Wer braucht Singletons in PHP
– Gordon
14. April 2011 um 6:47 Uhr
@Andrew Instanziieren Sie dann keine zweite Instanz, die eine Verbindung zur Datenbank herstellt. Übergeben Sie diese Instanz dorthin, wo sie benötigt wird. Die Notwendigkeit für einen Singleton ist ein Code Smell. Mehr bei gooh.posterous.com/singletons-in-php
– Gordon
15. April 2011 um 7:02 Uhr
@Andrew Mmmmkay. Nichts für ungut, aber ich schlage vor, Sie besorgen sich ein Buch über Softwarequalität, bevor wir diese Diskussion fortsetzen. Singletons vereinfachen nicht, sondern erschweren die normale Wartung und Entwicklung. Tatsächlich ist es umgekehrt: Es sind Unit-Tests, die die Entwicklung vereinfachen und überhaupt erst ermöglichen.
– Gordon
15. April 2011 um 17:07 Uhr
@Andrew: Sie gehen jetzt davon aus, dass Sie nur eine Datenbankverbindung benötigen. Was passiert, wenn sich Ihre Anforderungen ändern und Sie eigentlich mit 2 Datenbankservern sprechen müssen? Ganz zu schweigen davon, wenn Sie Ihrem Team nicht vertrauen können, Dinge zu tun rechts, das Erstellen eines Singletons wird Ihnen nicht im Geringsten helfen. Machen Sie die Dinge von Anfang an richtig und holen Sie sich ein Team, dem Sie vertrauen können, und es wird Ihnen gut gehen.
– ircmaxell
15. April 2011 um 17:10 Uhr
Nur weil das Singleton überstrapaziert wurde, ist es noch lange kein schlechtes Muster, das vermieden werden sollte. Hasse den Singleton nicht. Manchmal ist es eine perfekte Lösung für ein bestimmtes Problem. Beginnen Sie besser damit, zu argumentieren, warum wir es nicht verwenden sollten, anstatt nur zu versuchen, es emotional zu entwerten.
– Gilles Lesire
11. Dezember 2015 um 10:49 Uhr