Building a Singleton Trait with PHP 5.4

允我心安 提交于 2019-11-27 12:15:32

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!