Я заметил странное поведение с синглотами в PHP, нет лучшего способа объяснить это, но с примером.
Предположим, у меня есть следующий одноэлементный класс:
class Singleton { protected function __construct() { // Deny direct instantion! } protected function __clone() { // Deny cloning! } public static function &Instance() { static $Instance; echo 'Class Echo'.PHP_EOL; var_dump($Instance); if (!isset($Instance)) { $Instance = new self; } return $Instance; } }
И следующая функция:
function Test($Init = FALSE) { static $Instance; if ($Init === TRUE && !isset($Instance)) { $Instance =& Singleton::Instance(); } echo 'Function Echo'.PHP_EOL; var_dump($Instance); return $Instance; }
И когда я использую следующее:
Test(TRUE); Test(); Singleton::Instance();
Выход:
Class Echo NULL Function Echo object(Singleton)#1 (0) { } Function Echo NULL Class Echo object(Singleton)#1 (0) { }
Как вы можете видеть, сохраненная ссылка внутри функции теряется после выполнения, даже если переменная является статической. Также обратите внимание, что статическая переменная внутри метода класса работает нормально.
Это должно быть нормально, или я делаю что-то неправильно?
Это поведение документировано :
Zend Engine 1, управляющий PHP 4, реализует статический и глобальный модификатор для переменных в терминах ссылок . Например, истинная глобальная переменная, импортированная внутри области функций с глобальным оператором, фактически создает ссылку на глобальную переменную. Это может привести к неожиданному поведению.
Такое поведение не изменилось с ZE1, и решение просто не назначать ссылку на статическую переменную, поэтому:
$Instance = Singleton::Instance();
Обновить
Я упростил проблему ниже:
function test2($init = false, $value = null) { static $test; if ($init) { $test =& $value; } var_dump($test); } $v = 1; test2(true, $v); test2();
Вывод:
int(1) NULL
Это нормальный шаблон для singleton в php. Обратите внимание, что $ instance является статическим свойством, а не статической переменной внутри функции.
У меня возникли проблемы с выяснением причин, но ваш вышеуказанный код можно устранить, выполнив одно из следующих действий.
1) Удалить ссылочное задание в тестовой функции ( $Instance =& Singleton::Instance();
становится $Instance = Singleton::Instance();
)
2) Удалите возврат по ссылке в методе экземпляра ( public static function &Instance()
становится public static function Instance()
)
3) Или как 1, так и 2.
class Singleton{ protected static $instance; protected function __construct(){ } protected function __clone(){ throw new Exception("Cannot clone a Singleton"); } public static function getInstance(){ if(!isset(static::$instance)){ static::$instance = new static(); } return static::$instance; } }