Подтвердить что ты не робот

Построение черты Singleton с PHP 5.4

Недавно мы обсудили, удалось ли построить 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?

4b9b3361

Ответ 1

Быстрое решение, которое мы нашли (спасибо чату!):

Если черта и класс определяют один и тот же метод, один из классов, если используется

Таким образом, черта 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();

Теперь var_dump производит ожидаемый результат:

Hi!
object(A)#1 (1) {
  ["foo"]=>
  int(1)
}

и новый сбой:

Fatal error: Call to private A::__construct() from invalid context in ...

Demo

Ответ 2

Я создал один момент назад, когда мне было скучно, пытаясь узнать черты. Он использует отражение и константу __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() и не должны использовать произвольную функцию в качестве конструктора.

Ответ 3

Дело в том, что тип возврата getInstance будет неоднозначным, поскольку он зависит от потребителя. Это дает слабую типизированную подпись метода. Например, это делает невозможным предоставление @comeurn в соответствии с типом потребителя в блоке doc метода getInstance.

Ответ 4

Это все, что вам нужно. Если вы хотите, чтобы вы могли использовать частный статический член, но нет реальной потребности... Протестировано, работает, несмотря на то, что вы можете подумать, что статический будет глобальным или чем-то:)

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;
}