2010-06-27 104 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; 
    } 
}