使用 PHP 5.4 构建单例特性

2022-08-30 17:47:14

我们最近讨论了是否可以构建PHP Traits,我们尝试了一种可能的实现,但在构建一个PHP Traits时遇到了问题。trait Singleton

这是一项学术练习。我知道单例在PHP中很少使用(如果不是说不),并且应该“只创建一个”,而只是为了探索特征的可能性:

<?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

重现: http://codepad.viper-7.com/NmP0nZ

问题是:有可能在PHP中创建单例特征吗?


答案 1

我们找到的快速解决方案(感谢聊天!

如果一个特征和一个类都定义了相同的方法,则使用类的一个

因此,仅当使用它的类未定义__construct()

特性:

<?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() {}    
}

消费类示例:

<?php    
class A  {
    use Singleton;

    protected function init() {
        $this->foo = 1;
        echo "Hi!\n";
    }
}

var_dump(A::getInstance());

new A();

var_dump现在生成预期的输出:

Hi!
object(A)#1 (1) {
  ["foo"]=>
  int(1)
}

并且新的失败:

Fatal error: Call to private A::__construct() from invalid context in ...

Demo


答案 2

不久前,当我无聊地试图学习特征时,我创建了一个。它使用反射和常量__CLASS__

特性:

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(){}
}

这样,您可以继续使用__construct() 方法,而不需要使用任意函数作为构造函数。


推荐