Расширение синглотонов в PHP

Я работаю в рамках веб-приложения, а часть его состоит из нескольких сервисов, которые реализованы как одиночные. Все они расширяют класс 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, как я хочу, но экземпляр службы. Я предполагаю, что проблема здесь – это ключевое слово self, используемое в конструкторе Service.

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

Solutions Collecting From Web of "Расширение синглотонов в PHP"

Код:

 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, добавьте это тоже:

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

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

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

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

Это фиксированный ответ Йохана. 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; } }