Расширение синглетонов в PHP - PullRequest
25 голосов
/ 27 июня 2010

Я работаю в фреймворке веб-приложений, и часть его состоит из ряда сервисов, все реализованные в виде одиночных приложений. Все они расширяют класс Service, где реализовано одноэлементное поведение, и выглядит примерно так:

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

Теперь, если у меня есть класс с именем FileService, реализованный следующим образом:

class FileService extends Service {
    // Lots of neat stuff in here
}

... вызов FileService :: getInstance () приведет не к экземпляру FileService, как я хочу, а к экземпляру Service. Я предполагаю, что проблема здесь в том, что ключевое слово «self» используется в конструкторе Service.

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

Ответы [ 4 ]

55 голосов
/ 27 июня 2010

код:

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();

Если вы используете PHP <5.3, добавьте это тоже: </p>

// 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'].'/', $callerLine, $matches);
        } while ($matches[1] === 'parent' && $matches[1]);

        return $matches[1];
    }
}
8 голосов
/ 29 июня 2010

Если бы я уделил больше внимания в 5.3 классе, я бы знал, как решить это сам.Используя новую функцию позднего статического связывания в PHP 5.3, я полагаю, что предложение Коронатуса можно упростить до следующего:

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() { }
}

Я опробовал его, и он работает как шарм.Pre 5.3 все еще совсем другая история.

3 голосов
/ 22 ноября 2017

Это исправленный ответ Йохана. PHP 5.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;
    }
}
1 голос
/ 19 июня 2018

Я нашел хорошее решение.

следующий мой код

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) { }

Вы можете обратиться http://php.net/manual/en/language.oop5.late-static-bindings.php для дополнительной информации

...