问题
We recently had a discussion if it was possible to build a trait Singleton
PHP Traits and we played around with it a possible Implementation but ran into issues with building one.
This is an academic exercise. I know that Singletons have very little - if not to say no - use in PHP and that one should 'just create one' but just for exploring the possibilities of traits:
<?php
trait Singleton
{
protected static $instance;
final public static function getInstance()
{
return isset(static::$instance)
? static::$instance
: static::$instance = new static;
}
final private function __construct() {
static::init();
}
protected function init() {}
final private function __wakeup() {}
final private function __clone() {}
}
class A {
use Singleton;
public function __construct() {
echo "Doesn't work out!";
}
}
$a = new A(); // Works fine
reproduce: http://codepad.viper-7.com/NmP0nZ
The question is: It is possible to create a Singleton Trait in PHP?
回答1:
Quick solution we've found (thanks chat!):
If a trait and a class both define the same method, the one of class if used
So the Singleton trait only works if the class that uses it doesn't define a __construct()
Trait:
<?php
trait Singleton
{
protected static $instance;
final public static function getInstance()
{
return isset(static::$instance)
? static::$instance
: static::$instance = new static;
}
final private function __construct() {
$this->init();
}
protected function init() {}
final private function __wakeup() {}
final private function __clone() {}
}
Example for a consuming class:
<?php
class A {
use Singleton;
protected function init() {
$this->foo = 1;
echo "Hi!\n";
}
}
var_dump(A::getInstance());
new A();
The var_dump now produces the expected output:
Hi!
object(A)#1 (1) {
["foo"]=>
int(1)
}
and the new fails:
Fatal error: Call to private A::__construct() from invalid context in ...
Demo
回答2:
I created one a while ago when i was bored trying to learn traits. It uses reflection and the __CLASS__
constant
Trait:
trait Singleton
{
private static $instance;
public static function getInstance()
{
if (!isset(self::$instance)) {
$reflection = new \ReflectionClass(__CLASS__);
self::$instance = $reflection->newInstanceArgs(func_get_args());
}
return self::$instance;
}
final private function __clone(){}
final private function __wakeup(){}
}
This way you can continue to use the __construct() method and don't need to use an arbitrary function as the constructor.
回答3:
The thing is that the type of getInstance return will be ambigous since it depends on the consumer. This gives a weak-typed method signature. For instance it makes it impossible to provide an @return in compliance with the consumer type in the getInstance method doc bloc.
回答4:
This is guys all what you need. If you wish you could use private static member, but there is no a real need... Tested, works despite the fact that you might think that static will be global or something :)
trait Singleton
{
/**
* Singleton pattern implementation
* @return mixed
*/
public static function Instance()
{
static $instance = null;
if (is_null($instance)) {
$instance = new self();
}
return $instance;
}
}
usage:
class MyClass
{
use Singleton;
}
回答5:
A bit late to the party, but I wanted to show how (in Eclipse Oxygen PDT at least) you can do the DocBlock where auto-completion will work for this
trait SingletonTrait{
/**
*
* @var self
*/
private static $Instance;
final private function __construct()
{
}
final private function __clone()
{
}
final private function __wakeup()
{
}
/**
*
* Arguments passed to getInstance are passed to init(),
* this only happens on instantiation
*
* @return self
*/
final public static function getInstance(){
if(!self::$Instance){
self::$Instance = new self;
self::$Instance->init();
}
return self::$Instance;
}
protected function init()
{
}
}
As you can see both $instance
and getInstance
are defined as self
. Eclipse is smart enough to work this out so that when you use it in a class all the auto-completion works just as normal.
Test
来源:https://stackoverflow.com/questions/7104957/building-a-singleton-trait-with-php-5-4