Ich arbeite in einem Web-App-Framework, und ein Teil davon besteht aus einer Reihe von Diensten, die alle als Singletons implementiert sind. Sie alle erweitern eine Service-Klasse, in der das Singleton-Verhalten implementiert ist, und sehen in etwa so aus:
class Service {
protected static $instance;
public function Service() {
if (isset(self::$instance)) {
throw new Exception('Please use Service::getInstance.');
}
}
public static function &getInstance() {
if (empty(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
}
Wenn ich nun eine Klasse namens FileService wie folgt implementiert habe:
class FileService extends Service {
// Lots of neat stuff in here
}
… das Aufrufen von FileService::getInstance() ergibt keine FileService-Instanz, wie ich es möchte, sondern eine Service-Instanz. Ich nehme an, das Problem hier ist das Schlüsselwort “self”, das im Service-Konstruktor verwendet wird.
Gibt es eine andere Möglichkeit, das zu erreichen, was ich hier will? Der Singleton-Code besteht nur aus wenigen Zeilen, aber ich möchte trotzdem jede Coderedundanz vermeiden, wann immer ich kann.
Amy B
Code:
abstract class Singleton
{
protected function __construct()
{
}
final public static function getInstance()
{
static $instances = array();
$calledClass = get_called_class();
if (!isset($instances[$calledClass]))
{
$instances[$calledClass] = new $calledClass();
}
return $instances[$calledClass];
}
final private function __clone()
{
}
}
class FileService extends Singleton
{
// Lots of neat stuff in here
}
$fs = FileService::getInstance();
Wenn Sie PHP < 5.3 verwenden, fügen Sie dies auch hinzu:
// get_called_class() is only in PHP >= 5.3.
if (!function_exists('get_called_class'))
{
function get_called_class()
{
$bt = debug_backtrace();
$l = 0;
do
{
$l++;
$lines = file($bt[$l]['file']);
$callerLine = $lines[$bt[$l]['line']-1];
preg_match('/([a-zA-Z0-9\_]+)::'.$bt[$l]['function']."https://stackoverflow.com/", $callerLine, $matches);
} while ($matches[1] === 'parent' && $matches[1]);
return $matches[1];
}
}
FYI: Dieser Code verwendet get_called_class, hinzugefügt in PHP 5.3. In früheren Versionen ist dies etwas schwieriger.
– Karl
27. Juni 2010 um 2:39 Uhr
Heilige Huch, das ist beängstigend. Stell dir vor, du rufst an getInstance ein Dutzend Mal, das sind ein Dutzend Öffnungen und ein Dutzend Lesevorgänge der Klassendatei.
– Karl
27. Juni 2010 um 3:01 Uhr
Aus diesem Grund sollten die Leute auf das neueste und beste upgraden ^^
– Amy B
27. Juni 2010 um 3:04 Uhr
Vielen Dank! Ich habe mir diese Funktion direkt angesehen, nachdem ich die Frage gestellt hatte, aber ich war mir nicht sicher, wie ich sie verwenden sollte, um das Problem zu lösen. Jetzt muss ich nur noch warten, bis die Web-Hotel-Jungs auf PHP5.3 upgraden 🙂
– Johan Fredrik Varen
27. Juni 2010 um 3:06 Uhr
@Johan Vielleicht möchten Sie die Singletons ganz fallen lassen. Sie führen eine harte Kopplung in Ihre Anwendung ein und sind schwer zu testen. Sie können das Problem, dass möglicherweise nur eine Instanz vorhanden ist, mit einem Dependency Injection Framework oder einer Registrierung lösen.
– Gordon
27. Juni 2010 um 10:46 Uhr
Johan Fredrik Varen
Hätte ich in der Klasse 5.3 mehr aufgepasst, hätte ich gewusst, wie ich das selbst lösen kann. Unter Verwendung der neuen späten statischen Bindungsfunktion von PHP 5.3 glaube ich, dass der Vorschlag von Coronatus wie folgt vereinfacht werden kann:
class Singleton {
protected static $instance;
protected function __construct() { }
final public static function getInstance() {
if (!isset(static::$instance)) {
static::$instance = new static();
}
return static::$instance;
}
final private function __clone() { }
}
Ich habe es ausprobiert, und es funktioniert wie ein Zauber. Pre 5.3 ist jedoch immer noch eine ganz andere Geschichte.
Es scheint, dass es nur ein einziges Feld gibt $instance für alle Unterklassen, also nur das Singleton der Klasse wo getInstance() zuerst aufgerufen wird, wird zurückgegeben.
– C-Otto
21. November 2013 um 19:12 Uhr
Das ist der naive Ansatz, der leider überhaupt nicht funktionieren kann, wie C-Otto bereits erwähnt hat. Abgestimmt: Mach das nicht zu Hause 😉
– Phil
10. März 2015 um 15:09 Uhr
Wie sie oben sagten … es ist falsch, dies gibt Ihnen die Instanz der ersten Klasse, die getInstance () verwendet hat
– Juan
2. Juni 2015 um 14:33 Uhr
Dies ist Johans Antwort behoben. PHP5.3+
abstract class Singleton
{
protected function __construct() {}
final protected function __clone() {}
final public static function getInstance()
{
static $instance = null;
if (null === $instance)
{
$instance = new static();
}
return $instance;
}
}
Ich habe eine gute Lösung gefunden.
Folgendes ist mein Code
abstract class Singleton
{
protected static $instance; // must be protected static property ,since we must use static::$instance, private property will be error
private function __construct(){} //must be private !!! [very important],otherwise we can create new father instance in it's Child class
final protected function __clone(){} #restrict clone
public static function getInstance()
{
#must use static::$instance ,can not use self::$instance,self::$instance will always be Father's static property
if (! static::$instance instanceof static) {
static::$instance = new static();
}
return static::$instance;
}
}
class A extends Singleton
{
protected static $instance; #must redefined property
}
class B extends A
{
protected static $instance;
}
$a = A::getInstance();
$b = B::getInstance();
$c = B::getInstance();
$d = A::getInstance();
$e = A::getInstance();
echo "-------";
var_dump($a,$b,$c,$d,$e);
#object(A)#1 (0) { }
#object(B)#2 (0) { }
#object(B)#2 (0) { }
#object(A)#1 (0) { }
#object(A)#1 (0) { }
Ich bin auf diese Frage gestoßen, weil ich eine Singleton-Klasse zum Verwalten eines Cache-ähnlichen Objekts verwende und es erweitern wollte. Die Antwort von Amy B sah für meinen Geschmack etwas zu kompliziert aus, also habe ich ein bisschen weiter gegraben und das ist, was ich herausgefunden habe, funktioniert wie ein Zauber:
abstract class Singleton
{
protected static $instance = null;
protected function __construct()
{
}
final public static function getInstance()
{
if (static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
final private function __clone()
{
}
}
class FileService extends Singleton
{
protected static $instance = null;
}
$fs = FileService::getInstance();
Durch einfaches Überschreiben der Klasseneigenschaft $instance wird das Problem behoben. Nur mit PHP 8 getestet, aber ich vermute, dass dies auch für ältere Versionen funktioniert.
Die Verwendung eines Merkmals anstelle einer abstrakten Klasse ermöglicht die Erweiterung einer Singleton-Klasse.
Verwenden Sie die Eigenschaft SingletonBase für eine übergeordnete Singleton-Klasse.
Verwenden Sie das Merkmal SingletonChild für seine untergeordneten Singletons.
interface Singleton
{
public static function getInstance(): Singleton;
}
trait SingletonBase
{
private static $instance=null;
abstract protected function __construct();
public static function getInstance(): Singleton {
if (is_null(self::$instance)) {
self::$instance=new static();
}
return self::$instance;
}
protected function clearInstance(): void {
self::$instance=null;
}
public function __clone()/*: void*/ {
trigger_error('Class singleton '.get_class($this).' cant be cloned.');
}
public function __wakeup(): void {
trigger_error('Classe singleton '.get_class($this).' cant be serialized.');
}
}
trait SingletonChild
{
use SingletonBase;
}
class Bar
{
protected function __construct(){
}
}
class Foo extends Bar implements Singleton
{
use SingletonBase;
}
class FooChild extends Foo implements Singleton
{
use SingletonChild; // necessary! If not, the unique instance of FooChild will be the same as the unique instance of its parent Foo
}
Stanislaw Stankow
private static $_instances = [];
/**
* gets the instance via lazy initialization (created on first usage).
*/
public static function getInstance():self
{
$calledClass = class_basename(static::class);
if (isset(self::$_instances[$calledClass])) {
self::$_instances[$calledClass] = new static();
}
return self::$_instances[$calledClass];
}
Das einzige Problem mit diesem ist, wenn Sie Singletons mit dem gleichen Namen haben.
11147400cookie-checkErweitern von Singletons in PHPyes