Создание базового класса Singleton в PHP 5.3

Прямо к точке: У меня есть два одноэлементных класса, оба наследуют их одноэлементную природу от суперкласса. Я инициализирую некоторые свойства первого синглтона, а затем второй синглтон извлекает экземпляр первого. Однако этот экземпляр, похоже, не тот, который я инициализировал в первую очередь. Некоторые примеры кода могут помочь объяснить это:

Во-первых, суперкласс, обеспечивающий одноэлементный характер (требуется PHP 5.3 или выше):

class Singleton {

    protected static $instance;

    protected function __construct() { }

    final private function __clone() { }

    public static function getInstance() {
        if (!(static::$instance instanceof static)) {
            static::$instance = new static();
        }
        return static::$instance;
    }

}

Затем у нас есть первый синглтон, несущий значение:

require_once('Singleton.php');

class SingletonA extends Singleton {

    protected $value;

    public function SingletonA() {
        $this->value = false;
    }

    public function getValue() {
        return $this->value;
    }

    public function setValue($value) {
        $this->value = $value;
    }

}

Затем второй синглтон, который ссылается на первый синглтон:

require_once('Singleton.php');
require_once('SingletonA.php');

class SingletonB extends Singleton {

    public function getValue() {
        return SingletonA::getInstance()->getValue();
    }

}

Теперь для теста, который показывает, как это не удается:

require_once('SingletonA.php');
require_once('SingletonB.php');

SingletonA::getInstance()->setValue(true);

echo (SingletonA::getInstance()->getValue()) ? "true\n" : "false\n";
echo (SingletonB::getInstance()->getValue()) ? "true\n" : "false\n";

Тест дает следующий результат:

true
false

Очевидно, что экземпляр SingletonA, который ссылается на тестовый код, не является тем же экземпляром, что и экземпляр SingletonB. Короче говоря, SingletonA не такой единый, как мне нужно. Как это возможно? И какую магию я могу использовать, чтобы исправить это поведение, дав мне настоящий синглтон?

Ответ 1

Попробуйте использовать isset, а не instanceof:

class Singleton {
    protected static $instances;

    protected function __construct() { }

    final private function __clone() { }

    public static function getInstance() {
        $class = get_called_class();

        if (!isset(self::$instances[$class])) {
            self::$instances[$class] = new $class;
        }
        return self::$instances[$class];
    }
}

Ответ 2

Я почти уверен, потому что вы используете статические методы, которые не заданы.

Ответ 3

SingletonA и SingletonB - разные классы. Хотя они наследуются от одного класса, они являются отдельными классами и поэтому имеют разные статические экземпляры.

Если вы измените код, чтобы получить 2 экземпляра SingletonA или 2 экземпляра SingletonB, вы увидите поведение, которое вы ожидаете. Но поскольку они разные классы, они не являются одинаковыми синглтонами.

Ответ 4

Позвольте говорить OO.:)

SingletonA и SingletonB имеют тип Singleton

таким образом можно сказать:

SingletonA Singleton

и

SingletonB Singleton

то есть. они оба Singleton

Ожидаемый смысл Singleton означает, что может быть только один. Многие люди из OO-фона, используя ваш код, будут смущены.

Обычно реализация Singleton будет на основе каждого класса, потому что большинство языков OO не будут согнуты, чтобы позволить намерение того, что вы предлагаете.

То, что PHP может сделать (через get_called_class() magic), не означает, что он должен.

Я абсолютно согласен с тем, что с утилитарной точки зрения принятый ответ выглядит хорошо. Учитывая превосходство принятого ответа, я бы предложил изменение имени, которое не противоречит "стандартной" реализации Singleton. С точки зрения строгого ОО, нельзя было унаследовать от Синглтона, поэтому ему действительно нужно другое имя.