Building a Singleton Trait with PHP 5.4

后端 未结 5 2032
温柔的废话
温柔的废话 2020-12-14 02:22

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

相关标签:
5条回答
  • 2020-12-14 02:42

    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;
    }
    
    0 讨论(0)
  • 2020-12-14 02:49

    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.

    0 讨论(0)
  • 2020-12-14 02:49

    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.

    0 讨论(0)
  • 2020-12-14 02:49

    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

    0 讨论(0)
  • 2020-12-14 02:51

    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

    0 讨论(0)
提交回复
热议问题