Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending singletons in PHP

I'm working in a web app framework, and part of it consists of a number of services, all implemented as singletons. They all extend a Service class, where the singleton behaviour is implemented, looking something like this:

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

Now, if I have a class called FileService implemented like this:

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

... calling FileService::getInstance() will not yield a FileService instance, like I want it to, but a Service instance. I assume the problem here is the "self" keyword used in the Service constructor.

Is there some other way to achieve what I want here? The singleton code is only a few lines, but I'd still like to avoid any code redundance whenever I can.

like image 964
Johan Fredrik Varen Avatar asked Jun 27 '10 02:06

Johan Fredrik Varen


People also ask

Can I extend Singleton class?

All you need to extend a singleton class is a constructor with protected or package-default in the singleton class. If there are only private constructors you simply won't be able to extend it. If there are public constructors then it's not a singleton class.

Is it OK to use singletons?

The truth is that singletons aren't inherently bad if they're used correctly. The goal of the singleton pattern is to ensure only one instance of a class is alive at any one time. That, however, is not the goal many developers have in mind when using singletons.

What is the best way to subclass singletons?

I would argue the most common way to implement a singleton is to use an enum with one instance. That might be a "better" way but definitely not the most common way. In all the projects I have worked on, Singletons are implemented as I have shown above.

Can you modify a singleton?

Can you modify a singleton? Multiple Singletons Created by a Factory Specially Asked to Create Multiple Objects. One of the strengths of the Singleton design pattern, as opposed to static methods, is that if you change your mind and want more than one, the Singleton class can be easily altered.


2 Answers

Code:

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

If you use PHP < 5.3, add this too:

// 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];     } } 
like image 152
Amy B Avatar answered Oct 03 '22 19:10

Amy B


Had I paid more attention in 5.3 class, I would have known how to solve this myself. Using the new late static binding feature of PHP 5.3, I believe Coronatus' proposition can be simplified into this:

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

I tried it out, and it works like a charm. Pre 5.3 is still a whole different story, though.

like image 36
Johan Fredrik Varen Avatar answered Oct 03 '22 21:10

Johan Fredrik Varen