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