php 破坏单例模式(没有绝对的单例模式)

679 阅读1分钟

在大家写一个单例模式的代码时,很容易就会写出以下的代码

class s
{
    private static $single;

    private function __construct()
    {
    }

    public static function getSingle()
    {
        if(!self::$single){
            self::$single = new self();
        }

        return self::$single;
    }
    
    //年长一点的同学还会写出以下的魔术方法来阻止 深度复制 $s1 = clone $s2
    private function __clone()
    {
    }
    
}

$s = s::getSingle();

这样就足够了,很明显并不是,因为我发现了以下两种无解的破坏方式

首先派出的就是: 。。。 就决定是你了序列化 serializeunserialize

$s = s::getSingle();
$b1 = unserialize(serialize($s));
var_dump($s === $b1); //false
var_dump($b1 === s::getSingle()); //false
var_dump($s === s::getSingle()); //true

此时我们发现序列化出来的$b1并不是单例了,这个很多人估计工作中也会忘记吧,那我们就把 __sleep__wakeup都设置为私有,这样就不会出现破坏单例吧,然而现实不允许呢,然后我发现了下面的测试,在我尝试在类中加入了__wakeup方法

   public function __wakeup()
    {
        self::$single = static::$single;
    }

然后测试

$s = s::getSingle();
$b1 = unserialize(serialize($s));
var_dump($b1 === s::getSingle()); //false 
var_dump($s === s::getSingle()); //true

看来反序列化的时候,静态变量$single的值已经发生了改变,导致每次反序列化的时候就相当于新建了对象

猜测和我接下来说的第二种方式反射有关,其中的newinstancewithoutconstructor更加无解,直接就不使用你的构造函数就可以实例化了

$r = new ReflectionClass('s');
$d1 = $r->newInstanceWithoutConstructor();
var_dump($s === $d1); //false
var_dump($d1 === s::getSingle()); //false

具体原理本人也不懂,希望懂的人可以留言解答以下,万分感谢