Yet Another Singleton Class in PHP (<5.3)
class SingletonException extends Exception
{}
abstract class Singleton {
/**
* Is the repository for instances of subclasses
*
* @var array
*/
static private $_instances = array();
/**
* 1: Disables the 'new' operation
* 2: Calls Singleton::_construct() with all the arguments passed to
* Singleton::instance(), except the first one, which is the class name
* 3: Creates a shortcut for accessing the instance by the class name
*
* @param array $args
*/
private function __construct( $args )
{
$init = array($this, '_construct');
call_user_func_array( $init, $args );
$class = get_class($this);
$shortcut = "
if (! function_exists('$class'))
{
function $class()
{
return Singleton::instance('$class');
}
}
";
eval($shortcut);
}
/**
* Disables the 'clone' operation
*/
private function __clone()
{}
/**
* 1: Initializes the instance
* 2: Gets called once and automatically by Singleton::instance()
* 3: Receives all the arguments passed to Singleton::instance(), except the
* first one, which is the class name
*/
protected function _construct()
{}
/**
* Returns true if there is an instance of the $class in the repository
*
* @param string $class
* @return boolean
*/
static public function instanceAvailable( $class )
{
return isset( self::$_instances[ $class ] );
}
/**
* 1: Returns the instance of the $class
* 2: Creates, initializes and checks the instance the first time it gets
* called with a given $class name
*
* @param string $class
* @return Singleton
*/
static public function instance( $class )
{
if (! self::instanceAvailable( $class ))
{
$args = array_slice( func_get_args(), 1 );
$instance = new $class( $args );
if ($instance instanceof self)
{
self::$_instances[ $class ] = $instance;
}
else
{
throw new SingletonException('Expected a Singleton subclass');
}
}
return self::$_instances[ $class ];
}
}
Example
require_once "singleton.php";
/**
* Basic
*/
class C extends Singleton {};
/**
* Value
*/
class A extends Singleton
{
protected $val = null;
public function set($val)
{
$this->val = $val;
}
public function get()
{
return $this->val;
}
protected function _construct( $a )
{
$this->set($a);
}
}
/**
* Extended Value
*/
class AA extends A
{
protected function _construct( $a, $b )
{
$this->set(C()->hello . ($a + $b->get()));
}
}
//$test = new A();
//Fatal error: Call to private Singleton::__construct() from invalid context...
Singleton::instance('C')->hello = 'world';
echo "<p>Now C holds " . C()->hello . "</p>";
Singleton::instance('A', 3);
echo "<p>Now A holds " . A()->get() . "</p>";
Singleton::instance('A', 5);
Singleton::instance('AA', 7, A());
echo "<p>Now AA holds " . AA()->get() . "</p>";
A()->set(5);
echo "<p>Now A holds " . A()->get() . "</p>";
AA()->set(5);
echo "<p>Now AA holds " . AA()->get() . "</p>";
$result = AA() === A() ? 'Yes' : 'No';
echo "<p>AA() is A() ? $result</p>";
$result = Singleton::instance('A') === A() ? 'Yes' : 'No';
echo "<p>Singleton::instance('A') is A() ? $result</p>";
Results
Now C holds world
Now A holds 3
Now AA holds world10
Now A holds 5
Now AA holds 5
AA() is A() ? No
Singleton::instance('A') is A() ? Yes