2

I have this code running on PHP 5.2.6

class Singleton { static private $instance = false; private $id = false; protected function __construct() { $this->id = uniqid(); } static public function instance() { if (!self :: $instance) { self :: $instance = new self(); } return self :: $instance; } public function get_id() { return $this->id; } } class Chucknorris extends Singleton { } echo "php version = ".phpversion()."<br>"; $singleton = Singleton::instance(); echo get_class($singleton)."<br>"; echo "singleton id = ".$singleton->get_id()."<br>"; $chucknorris = Chucknorris::instance(); echo get_class($chucknorris)."<br>"; echo "chucknorris id = ".$chucknorris->get_id()."<br>"; 

Here's the output

php version = 5.2.6 Singleton singleton id = 4ea7dca7d8f23 Singleton chucknorris id = 4ea7dca7d8f23 

When I ask for an instance of Chucknorris, I always get the Singleton one. I'd like to find out a way to extend the Singleton.

I know we can use get_called_class method to do it but it comes only with PHP 5.3. Is there anyway I can extend a Singleton without redefining the design pattern in the extended classes ?

2
  • 3
    if you can upgrade your PHP version to 5.3, than you can use static:: instead of self:: Than, you can declare your static instance on the child class, but static:: will call the child variable, while self:: will call the one in the current defined class. Commented Oct 26, 2011 at 12:46
  • Maybe this is an interesting article? weierophinney.net/matthew/archives/… Commented Oct 26, 2011 at 12:48

4 Answers 4

3

Your best bet in PHP < 5.3 is to use a Singleton Factory:

class Singleton { private $id = false; public function __construct() { $this->id = uniqid(); } public function get_id() { return $this->id; } } class SingletonFactory { private static $instance_array = array(); public static function getInstance($class_name) { if (!isset(self::$instance_array[$class_name])) { self::$instance_array[$class_name] = new $class_name(); } return self::$instance_array[$class_name]; } } class Chucknorris extends Singleton {} $singleton = SingletonFactory::getInstance('Singleton'); echo get_class($singleton)."<br>"; echo "singleton id = ".$singleton->get_id()."<br>"; $chucknorris = SingletonFactory::getInstance('Chucknorris'); echo get_class($chucknorris)."<br>"; echo "chucknorris id = ".$chucknorris->get_id()."<br>"; 

The only downside here is that your Singleton constructor is public.. so that's a basic violation of that pattern.

Update: Here's a version that removes the public constructor (warning: this is getting into messy/hacky/poor design territory)

class Singleton { private $id = false; public function __construct() { $back = debug_backtrace(false); if (!isset($back[1]['class']) || $back[1]['class'] != 'SingletonFactory') { throw new Exception('Consturctor not available, use SingletonFactory::getInstance("CLASSNAME")'); } $this->id = uniqid(); } public function get_id() { return $this->id; } } class SingletonFactory { private static $instance_array = array(); public static function getInstance($class_name) { if (!isset(self::$instance_array[$class_name])) { self::$instance_array[$class_name] = new $class_name($class_name); } return self::$instance_array[$class_name]; } } class Chucknorris extends Singleton {} $singleton = SingletonFactory::getInstance('Singleton'); echo get_class($singleton)."<br>"; echo "singleton id = ".$singleton->get_id()."<br>"; $chucknorris = SingletonFactory::getInstance('Chucknorris'); echo get_class($chucknorris)."<br>"; echo "chucknorris id = ".$chucknorris->get_id()."<br>"; $badchuck = new Chucknorris(); // Exception! 
Sign up to request clarification or add additional context in comments.

2 Comments

you are on the right track. However the Singleton doesn't have to have a public constructor. It could be done with a static function called getNewInstance which fires the constructor each time it is called.
Yep! It's not great -- I did mention the lack of a public constructor in the original answer though. Note, getNewInstance() would have to be defined in each subclass of Singleton -- which would defy the purpose.
2

Why don't you simulate the get_class_function if it doesn't exist with 5.3 PHP version ? This code may answer your question.

if (!function_exists('get_called_class')) { function get_called_class() { $bt = debug_backtrace(); $lines = file($bt[1]['file']); preg_match( '/([a-zA-Z0-9\_]+)::'.$bt[1]['function'].'/', $lines[$bt[1]['line']-1], $matches ); return $matches[1]; } } abstract class Singleton { private $id = false; protected function __construct() { $this->id = uniqid(); } static public function instance() { static $instances = array(); $called_class_name = get_called_class(); if (!isset($instances[$called_class_name])) { $instances[$called_class_name] = new $called_class_name(); } return $instances[$called_class_name]; } public function get_id() { return $this->id; } } class Chucknorris extends Singleton {} class Brucelee extends Singleton {} echo "php version = ".phpversion()."<br>"; $chucknorris = Chucknorris::instance(); echo get_class($chucknorris)."<br>"; echo "chucknorris id = ".$chucknorris->get_id()."<br>"; $brucelee = Brucelee::instance(); echo get_class($brucelee)."<br>"; echo "brucelee id = ".$brucelee->get_id()."<br>"; 

Comments

1

You can redefine just the getinstance method (and the instance itself) in Chucknorris to get an instance of it instead of the parent, but I'm not exactly sure what your end goal is. Just change the extending class to:

class Chucknorris extends Singleton { static private $instance = false; static public function instance() { if (!self :: $instance) { self :: $instance = new self(); } return self :: $instance; } } 

Is this what you want? And if so - what is the reason you want it? I could think of a few, but would be glad if you share you goal.

3 Comments

i think the ideea of extending is not to write again the same code.
Only the getinstance method is the same - everything else can be extended.
@DanBizdadea: If the OP is looking for late static binding, then PHP 5.2.x is the wrong PHP version.
0

Your code will most likely work if you move static private $instance = false; to the subclass and make it protected instead of private. You also need to replace self:: with static:: so the static var is set in the subclass. This requires PHP 5.3 - however, this shouldn't be a problem because PHP 5.2 reached end-of-life/support (that includes security updates!) as of january 2011!

3 Comments

And don't forget to copy the instance() function.
I moved this line to the subclass, I got Fatal error: Access to undeclared static property: Singleton::$instance It's normal as Singleton object tried to modify this property. Same thing when I copied it in the subclass.
Make it protected instead of private.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.