Недавно у нас было обсуждение, если можно было создать trait Singleton
PHP Traits, и мы играли с ней в возможной реализации, но столкнулись с проблемами со строительством.
Это академическое упражнение. Я знаю, что у Singletons have very little - if not to say no - use in PHP
и что one should 'just create one'
а просто для изучения возможностей черт:
<?php trait Singleton { protected static $instance; final public static function getInstance() { return isset(static::$instance) ? static::$instance : static::$instance = new static; } final private function __construct() { static::init(); } protected function init() {} final private function __wakeup() {} final private function __clone() {} } class A { use Singleton; public function __construct() { echo "Doesn't work out!"; } } $a = new A(); // Works fine
воспроизводить: http://codepad.viper-7.com/NmP0nZ
Вопрос в том, можно ли создать черту Singleton в PHP?
Быстрое решение, которое мы нашли (спасибо чату!):
Если признак и класс определяют один и тот же метод, один из классов, если используется
Таким образом, свойство Singleton работает только в том случае, если класс, который его использует, не определяет __construct()
<?php trait Singleton { protected static $instance; final public static function getInstance() { return isset(static::$instance) ? static::$instance : static::$instance = new static; } final private function __construct() { $this->init(); } protected function init() {} final private function __wakeup() {} final private function __clone() {} }
<?php class A { use Singleton; protected function init() { $this->foo = 1; echo "Hi!\n"; } } var_dump(A::getInstance()); new A();
Hi! object(A)#1 (1) { ["foo"]=> int(1) }
Fatal error: Call to private A::__construct() from invalid context in ...
Demo
Я создал его некоторое время назад, когда мне было скучно, пытаясь узнать черты. Он использует отражение и постоянную __CLASS__
Черта:
trait Singleton { private static $instance; public static function getInstance() { if (!isset(self::$instance)) { $reflection = new \ReflectionClass(__CLASS__); self::$instance = $reflection->newInstanceArgs(func_get_args()); } return self::$instance; } final private function __clone(){} final private function __wakeup(){} }
Таким образом, вы можете продолжать использовать метод __construct () и не должны использовать произвольную функцию в качестве конструктора.
Дело в том, что тип возврата getInstance будет неоднозначным, поскольку он зависит от потребителя. Это дает слабую типизированную подпись метода. Например, это делает невозможным предоставление @ comeurn в соответствии с типом потребителя в блоке doc метода getInstance.
Это все, что вам нужно. Если вы хотите, чтобы вы могли использовать частный статический член, но нет реальной потребности … Протестировано, работает, несмотря на то, что вы можете подумать, что статический будет глобальным или чем-то 🙂
trait Singleton { /** * Singleton pattern implementation * @return mixed */ public static function Instance() { static $instance = null; if (is_null($instance)) { $instance = new self(); } return $instance; } }
Применение:
class MyClass { use Singleton; }