php 破壞單例模式(沒有絕對的單例模式)

在大家寫一個單例模式的代碼時,很容易就會寫出以下的代碼

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

具體原理本人也不懂,希望懂的人可以留言解答以下,萬分感謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章