Serialize Unserialize破壞單例

2021-07-12 07:30:53 字數 2798 閱讀 3995

我們經常採用如下方式定義單例

class

singleton

/** 不容許深度複製 */

private

function

__clone

()

public

static

function

getinstance

()

return

self::$instance;

} }

很多人都會記得對深度copy的保護, 但, 其實我們卻疏忽了一點

$a = singleton::getinstance();

$b = unserialize(serialize($a));

var_dump($a === $b);

//bool(false)

呵呵, 可見還需要修補, 加上對序列化的保護

class

singleton

/** 不容許深度複製 */

private

function

__clone

() /** 不容許serialize */

private

function

__sleep

() /** 不容許unserialize */

private

function

__wakeup

() public

static

function

getinstance

() return

self::$instance;

}}

然而, 有的時候我們是希望我們的單例類是能序列化的, 這個時候可以考慮如下的方式

class

singleton

/** 不容許深度複製 */

private

function

__clone

() public

function

__wakeup

() /** 需要在單例切換的時候做清理工作 */

public

function

__destruct

() public

static

function

getinstance

() return

self::$instance;

}}

請注意上面, 我們在wakeup的時候, 切換了當前的單例例項, 來實現在序列化/反序列化的時刻保證單例。

另外, 對於一些包含全域性資源的單例類, 我們需要定義析構函式, 來在切換的過程中做資源**工作。

現在, 請大家仔細看看, 然後想想這段**有沒有什麼問題?

接著往下看, 這段**在有些條件下, 可能會達不到我們預期的目標, 比如

$a = singleton::getinstance();  

$a = unserialize(serialize($a));

var_dump($a === singleton::getinstance());

//bool(false)

那麼為什麼呢?

我之前的文章深入理解php原理之變數分離/引用(variables separation) 中曾經介紹過, 在php中, 採用引用計數的方式來減少對記憶體的使用和提高效率。

回頭來看這個問題, 根據運算子的結合律, 我們來單步分析這個過程:

在我們呼叫unserialize(serialize($a))的時候, 在serialize之前, php會首先嘗試呼叫我們的類的例項$a的__sleep方法, 因為我們沒有定義此方法, 所以跳過此步驟..

接下來, 在unserialize的時候, php在完成物件的建立以後, 會來呼叫新建立物件的__wakeup方法 , 在這裡面, 我們釋放了原有的self::$instance的引用, 改變成了新的物件.

這個時候, 原來的$a, 並不會被釋放, 因為此時符號名a還保留著對$a(單例類的乙個例項)的引用, 但此時$a所指的物件的引用計數已經-1, 變成了1, (應該還要了解到, 此時, 還會對object store中的物件引用計數-1, 也變為了1)

最後, 我們把得到的新物件給$a賦值, ok, 關鍵的時候來了, 這個時候, 因為我們重新對$a賦值, 所以$a會釋放之前所值向的zval的引用, 造成了此時這個zval的引用計數變為了零, 於是php就會釋放這個zval, 也就會呼叫了singleton的析構函式, 在這個析構函式中, 我們釋放了靜態例項$instance..

現在明白了麼?

當然, 最後寫成這樣

class

singleton

/** 不容許深度複製 */

private

function

__clone

() public

function

__wakeup

() /** 需要在單例切換的時候做清理工作 */

public

function

__destruct

() public

static

function

getinstance

() return

self::$instance;

}}

Serialize Unserialize破壞單例

作者 laruence 我們經常採用如下方式定義單列 1.class singleton 8.9.不容許深度複製 10.private function clone 12.13.public static function getinstance 17.18.return self instance...

單例模式,解決單例破壞。

破壞單例模式的三種方法 執行緒安全情況下 單例模式有 3 個特點 單例類只有乙個例項物件 該單例物件必須由單例類自行建立 單例類對外提供乙個訪問該單例的全域性訪問點。單例模式的優點和缺點 單例模式的優點 單例模式可以保證記憶體裡只有乙個例項,減少了記憶體的開銷。可以避免對資源的多重占用。單例模式設定...

C 反射破壞單例

單例大家都不陌生,程式從開啟到死亡過程中只能存在乙個例項,即存在不可建立,今天給大家介紹一種打破這種模式的方法,在程式執行中建立無數個單例例項物件。關於單例模式模糊或者不懂的可以參考c 單例模式 上文我們講到了反射的基本操作,例項化物件 屬性 方法 特性等操作,可以參考c 反射 咱們切入正題,單例有...