当前位置: 首页 > 后端技术 > PHP

序列化-反序列化销毁单例

时间:2023-03-30 00:14:51 PHP

看了鸟哥的博客,一直认为单例的常规定义方式是正确的。看了博客,发现序列化可以破坏单例。原文地址:laruencehttp://www.laruence.com定义单例的常规方式:classSingleton{privatestatic$instance=NULL;/**不允许直接调用构造函数*/privatefunction__construct(){}/**不允许深度复制*/privatefunction__clone(){}publicstaticfunctiongetInstance(){if(NULL===self::$instance){self::$instance=newself();返回自我::$实例;}}序列化/反序列化销毁单例$a=Singleton::getInstance();$b=反序列化(序列化($a));var_dump($a===$b);//bool(false)修改1(防止序列化/反序列化)classSingleton{privatestatic$instance=NULL;/**不允许直接调用构造函数*/privatefunction__construct(){}/**不允许深度复制*/privatefunction__clone(){}/**不允许序列化*/privatefunction__sleep(){}/**不允许反序列化*/privatefunction__wakeup(){}publicstaticfunctiongetInstance(){if(NULL===self::$instance){self::$instance=newself();}返回自我::$实例;}}修改2(允许序列化/反序列化,并保证单例)classSingleton{privatestatic$instance=NULL;/**不允许直接调用构造函数*/privatefunction__construct(){}/**不允许深度复制*/privatefunction__clone(){}publicfunction__wakeup(){self::$instance=$this;}/**切换单例时需要做清理*/publicfunction__destruct(){self::$instance=NULL;}publicstaticfunctiongetInstance(){if(NULL===self::$instance){self::$instance=newself();}返回自我::$实例;}}修改2有问题$a=Singleton::getInstance();$a=反序列化(序列化($a));var_dump($a===Singleton::getInstance());//bool(false)修改2原因当我们调用unserialize(serialize($a))时,在serialize之前,PHP会先尝试调用我们类实例$a的__sleep方法,因为我们还没有定义这个方法,所以跳过这一步。接下来unserialize时,PHP会在完成对象的创建后调用新创建的对象的__wakeup方法,在该方法中我们释放了self::$instance的原始引用并将其更改为新对象。这个时候原来的$a是不会被释放的,因为这个时候符号名a还保留着$a的引用(单例类的一个实例),但是这个时候对象的引用计数$a指向的一直是-1,变成了1。Store中的对象引用计数是-1,也变成了1)最后,我们把新对象赋值给$a,OK,关键时刻到了,这个时候,因为我们重新给$a赋值,$a会释放之前赋值的zval的引用,导致此时这个zval的引用计数变为0,所以PHP会释放这个zval,调用单例析构函数。在这个析构函数中,我们释放了静态实例$instance..最后的修改方案classSingleton{privatestatic$instance=NULL;/**不允许直接调用构造函数*/privatefunction__construct(){}/**不允许深度复制*/privatefunction__clone(){}publicfunction__wakeup(){self::$instance=$这;}/**单例切换时需要做清理*/publicfunction__destruct(){//只做清理}publicstaticfunctiongetInstance(){if(NULL===self::$instance){self::$instance=新的自我();}返回自我::$实例;}}