Estendendo singletons em PHP

Estou trabalhando em uma estrutura de aplicativo web, e parte dela é constituída por uma série de serviços, todos implementados como singletons. Todos eles estendem uma class de Serviço, onde o comportamento singleton é implementado, procurando algo assim:

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

Agora, se eu tiver uma class chamada FileService implementada assim:

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

… chamar FileService :: getInstance () não irá ceder uma instância FileService, como eu quero, mas uma instância do Service. Eu suponho que o problema aqui é a palavra-chave “self” usada no Construtor de serviços.

Existe alguma outra maneira de alcançar o que eu quero aqui? O código singleton é apenas algumas linhas, mas eu ainda gostaria de evitar qualquer redundância de código sempre que puder.

Código:

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

Se você usa PHP <5.3, adicione isso também:

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

Se eu tivesse prestado mais atenção na class 5.3, eu teria sabido como resolver isso sozinho. Usando o novo recurso de binding estática tardia do PHP 5.3, acredito que a proposição de Coronatus pode ser simplificada para isso:

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

Eu tentei, e isso funciona como um encanto. Pre 5.3 ainda é uma história completamente diferente.

Esta é a resposta fixa de Johan. 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; } } 
    Intereting Posts