Serialize/Unserialize破坏单例
来源:互联网 发布:戴尔ubuntu怎么装系统 编辑:程序博客网 时间:2024/05/08 13:46
- 作者: laruence http://www.laruence.com
- 本文地址: http://www.laruence.com/2011/03/18/1909.html
我们经常采用如下方式定义单例
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的保护, 但, 其实我们却疏忽了一点
$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)
那么为什么呢?
我之前的文章深入理解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 static $instance = NULL; /** 不容许直接调用构造函数 */ private function __construct() { } /** 不容许深度复制 */ private function __clone() { } public function __wakeup() { self::$instance = $this; } /** 需要在单例切换的时候做清理工作 */ public function __destruct() { //只做清理工作 } public static function getInstance() { if (NULL === self::$instance) { self::$instance = new self(); } return self::$instance; }}
- Serialize/Unserialize破坏单例
- Serialize/Unserialize破坏单例
- Serialize/Unserialize破坏单例
- serialize unserialize
- serialize()与unserialize()
- php serialize & unserialize
- php serialize & unserialize .
- php serialize和unserialize
- serialize() , unserialize()函数
- serialize unserialize 序列化
- php serialize(),unserialize()
- serialize与unserialize
- 破坏单例模式
- php函数serialize()与unserialize()
- php函数serialize()与unserialize()
- php函数serialize()与unserialize()
- php函数serialize()与unserialize()
- serialize()与unserialize()不完全研究
- 算法9—两个巨大正整数相加
- 自适应网页设计 or 响应式Web设计
- 基准测试V1.1
- springMVC+mybatis+ehcache详细配置
- 2 微信开发者中心
- Serialize/Unserialize破坏单例
- 记录一下vs2008调试dll时无法进到断点的解决方法
- java处理excel中图片(png,jpeg,jpg)
- Linux之小程序进度条
- HTTP响应头和请求头信息对照表
- POJ1065 Wooden Sticks(贪心||DP)
- SVN错误:Attempted to lock an already-locked dir
- mysql 查询出某个字段的值不为空的语句
- 橡皮擦