Serialize/Unserialize破坏单例

2024-05-06 21:58

本文主要是介绍Serialize/Unserialize破坏单例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • 作者: 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破坏单例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/965494

相关文章

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

使用Spring Boot集成Spring Data JPA和单例模式构建库存管理系统

引言 在企业级应用开发中,数据库操作是非常重要的一环。Spring Data JPA提供了一种简化的方式来进行数据库交互,它使得开发者无需编写复杂的JPA代码就可以完成常见的CRUD操作。此外,设计模式如单例模式可以帮助我们更好地管理和控制对象的创建过程,从而提高系统的性能和可维护性。本文将展示如何结合Spring Boot、Spring Data JPA以及单例模式来构建一个基本的库存管理系统

C#设计模式(1)——单例模式(讲解非常清楚)

一、引言 最近在学设计模式的一些内容,主要的参考书籍是《Head First 设计模式》,同时在学习过程中也查看了很多博客园中关于设计模式的一些文章的,在这里记录下我的一些学习笔记,一是为了帮助我更深入地理解设计模式,二同时可以给一些初学设计模式的朋友一些参考。首先我介绍的是设计模式中比较简单的一个模式——单例模式(因为这里只牵涉到一个类) 二、单例模式的介绍 说到单例模式,大家第一

单例模式以及反射对单例模式的破坏及防御

单例模式(Singleton Pattern)是一种确保类在应用程序生命周期内只存在一个实例的设计模式。它不仅提供了全局访问点,还能节省内存、控制实例的生命周期。但常见的单例模式实现方式如饿汉式、懒汉式、双重校验锁、静态内部类等,虽然设计良好,但都容易被 Java 的反射机制所破坏。本文将介绍这些单例实现方式的优缺点、反射如何破坏它们的唯一性,以及如何防御这种破坏。 1. 单例模式的常见实现

线程--(1)单例模式创建

通过内部类创建单例模式 package com.flx.king.it_201707;/*** 公共:单例模式与多线程实现* * @author FuLX* @2017-7-26下午10:14:08* */public class Dome_02_SingletonAndManyThread {//1、定义一个内部类private static class Singletion{//创建单

1 单例模式(设计模式笔记)

1 单例模式 概述:使得一个类的对象成为系统中的唯一实例。 具体实现: 构造函数私有化 限制实例的个数 懒汉式(时间换空间) public class Singleton2 {public static Singleton2 singleton2;private Singleton2(){}public static Singleton2 getInstance() throws I

设计模式(2)-- 单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 注意: 1、单例类只能有一个实例。 2、单例类必须自己创建自己的唯一实例。 3、单

C++ | 单例设计模式(懒汉式单例模式源码|饿汉式单例模式)

点击上方"蓝字"关注我们 01、概念 >>> 单例设计模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来访问该实例。单例模式通常用于需要在整个应用程序中共享一个对象的场景,例如配置管理、日志记录、数据库连接池等。 02、场景 >>> 应用场景 配置管理:应用程序的配置信息通常只需要一个实例来管理,避免多个配置实例导致数据不

使用 `readResolve` 防止序列化破坏单例模式

单例模式是一种设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点。在 Java 中,我们常常通过私有化构造方法和提供静态访问方法来实现单例。然而,尽管这些手段可以有效防止类的实例化,反射和序列化依然能够破坏单例模式的唯一性。本文将重点讲解序列化如何破坏单例模式,以及如何通过 readResolve 方法来防止这种破坏。 1. 序列化和反序列化 序列化 是指将对象的状态转换为字节

python单例模式练习

单例设计模式是怎么来的? 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。 在面向对象的程序设计中,当业务并发量非常大时,那么就会出现重复创建相同的对象,每创建一个对象就会开辟一块内存空间,而这些对象其实是一模一样的,那么有没有办法使用得内存对象只创建一