Press "Enter" to skip to content

Serialize/Unserialize破坏单例

我们经常采用如下方式定义单列:

class Singleton {
    private static $instance = NULL;
    /** 不容许直接调用构造函数 */
    private function __construct() {
    }
    /** 不容许深度复制 */
    private function __clone() {
    }
    public static function getInstance() {
        if (NULL === self::$instance) {
        	self::$instance = new self();
		}
        return self::$instance;
    }
}

很多人都会记得对深度copy的保护, 但, 其实我们却疏忽了一点:

<?php
$a = Singleton::getInstance();
$b = unserialize(serialize($a));
var_dump($a === $b);
//bool(false)

呵呵, 可见还需要修补, 加上对序列化的保护:

class Singleton {
    private static $instance = NULL;
    /** 不容许直接调用构造函数 */
    private function __construct() {
    }
    /** 不容许深度复制 */
    private function __clone() {
    }
    /** 不容许serialize */
    private function __sleep() {
    }
    /** 不容许unserialize */
    private  function __wakeup() {
    }
    public static function getInstance() {
        if (NULL === self::$instance) {
        	self::$instance = new self();
		}
        return self::$instance;
    }
}

然而, 有的时候我们是希望我们的单利类是能序列化的, 这个时候可以考虑如下的方式:

class Singleton {
    private static $instance = NULL;
    /** 不容许直接调用构造函数 */
    private function __construct() {
    }
    /** 不容许深度复制 */
    private function __clone() {
    }
    public  function __wakeup() {
        self::$instance = $this;
    }
    /** 需要在单利切换的时候做清理工作 */
    public function __destruct() {
        self::$instance = NULL;
    }
    public static function getInstance() {
        if (NULL === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

请注意上面, 我们在wakeup的时候, 切换了当前的单例实例, 来实现在序列化/反序列化的时刻保证单例.
另外, 对于一些包含全局资源的单例类, 我们需要定义析构函数, 来在切换的过程中做资源回收工作.
现在, 请大家仔细看看, 然后想想这段代码有没有什么问题?
接着往下看, 这段代码在有些条件下, 可能会达不到我们预期的目标, 比如:

$a = Singleton::getInstance();
$a = unserialize(serialize($a));
var_dump($a === Singleton::getInstance());
//bool(false)

大家可以想想为什么这样,, 如果不想想的, 就看我的下一篇文章吧.
最后, 做个广告,,,, 在新浪微博关注我吧: http://t.sina.com.cn/laruence, 🙂

15 Comments

  1. ltobet1
    ltobet1 July 8, 2022

    ltobet
    Good I appreciated your work very thanks

  2. Tranch
    Tranch November 5, 2014

    鸟哥,最近经常 502 啊。

  3. Richard.Z
    Richard.Z May 22, 2013

    把实现的资源给重清了,看了以后还是受益匪浅~

  4. zhupp
    zhupp November 15, 2011

    LZ,请问对单例类进行serialize有什么实际应用吗?好像没怎么用过,也想不出来哪里可以用,求教。

  5. iterse's blog
    iterse's blog April 8, 2011

    恩,看懂了一半,谢谢分享!

  6. devil_si
    devil_si March 18, 2011

    博主您好,能推荐几本学习php的书籍吗 谢谢。

Comments are closed.