2010-06-27 103 views
21

我正在一个web应用程序框架中工作,其中的一部分包含许多服务,所有服务都以单例实现。它们都继承Service类,其中单行为实现的,看起来像这样:现在在PHP中扩展单例

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实例。我假设这里的问题是Service构造函数中使用的“self”关键字。

有没有其他方法可以实现我想要的?单例代码只有几行,但我仍然希望尽可能避免任何代码冗余。

回答

46

代码:

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]; 
    } 
} 
+0

供参考:该代码使用['get_called_class'](http://us2.php.net/manual/en/function.get-called-class.php)中,加入在PHP 5.3。在早期版本中做这件事有点棘手。 – Charles 2010-06-27 02:39:51

+0

谢谢查尔斯,更新了解决这个问题的答案。 – 2010-06-27 02:56:32

+1

圣骑,这是可怕的。想象一下,调用'getInstance'十次,这是十几次打开和十几次读取类文件。 – Charles 2010-06-27 03:01:57

7

如果我在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() { } 
} 

我尝试过了,它就像一个魅力。不过,5.3之前的故事仍然完全不同。

+4

似乎只有一个单一的字段'所有子类的实例',所以只返回调用getInstance()'的类的单例。 – 2013-11-21 19:12:55

+0

这是一种天真的方法,不幸的是,C-Otto已经提到过,这种方法根本无法工作。 Downvoted:不要在家里这样做;-) – Phil 2015-03-10 15:09:28

+0

正如他们上面说的..它的错误,这会给你第一类的实例使用getInstance() – Juan 2015-06-02 14:33:21

0

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