php 析构函数在流畅的界面下调用得太早

2024-01-06

我发现 php 析构函数有一个非常奇怪的事情:

基本上我有一个数据库管理类,它使用工厂加载适配器来定义应该加载哪个适配器(mysql、mysqli等)

我只会写下代码中有趣的部分,因为类本身更长,但代码不涉及当前的麻烦

该问题仅发生在 mysql 上(mysqli 和 pdo 工作得很好),但出于兼容性目的,摆脱 mysql 是没有问题的。

class manager
{
    private static $_instance;

    public static function getInstance()
    {
        return isset(self::$_instance) ? self::$_instance : self::$_instance = new self;
    }

    public function getStandaloneAdapter()
    {
        return new mysql_adapter(array('host'=>'127.0.0.1', 'username' => 'root', 'password' => '', 'dbname' => 'etab_21'));
    }
}

abstract class abstract_adapter
{
    protected $_params;
    protected $_connection;

    public function __construct($params)
    {
        $this->_params = (object)$params;
    }

    public function __destruct()
    {
        echo 'destructor<br/>';
        var_dump(debug_backtrace(false));
        $this->closeConnection();
    }

    abstract public function closeConnection();
}

class mysql_adapter extends abstract_adapter
{
    public function getConnection()
    {
        $this->_connect();

        if ($this->_connection) {
            // switch database
            $this->_useDB($this->_params->dbname);
        }

        return $this->_connection;
    }

    protected function _connect()
    {
        if ($this->_connection) {
            return;
        }

        // connect
        $this->_connection = mysql_connect(
            $this->_params->host,
            $this->_params->username,
            $this->_params->password,
            true
        );

        if (false === $this->_connection || mysql_errno($this->_connection)) {
            $this->closeConnection();
            throw new Mv_Core_Db_Exception(null, Mv_Core_Db_Exception::CONNECT, mysql_error());
        }

        if ($this->_params->dbname) {
            $this->_useDB($this->_params->dbname);
        }
    }

    private function _useDB($dbname)
    {
        return mysql_select_db($dbname, $this->_connection);
    }

    public function isConnected()
    {
        $isConnected = false;
        if (is_resource($this->_connection)) {
            $isConnected = mysql_ping($this->_connection);
        }
        return $isConnected;
    }

    public function closeConnection()
    {
        if ($this->isConnected()) {
            mysql_close($this->_connection);
        }
        $this->_connection = null;
    }
}

这是我正在运行的测试:

$sadb1 = manager::getInstance()->getStandaloneAdapter()->getConnection();
var_dump($sadb1);

我得到的输出:

destructor
array
  0 => 
    array
      'file' => string '**\index.php' (length=48)
      'line' => int 119
      'function' => string '__destruct' (length=10)
      'class' => string 'abstract_adapter' (length=16)
      'type' => string '->' (length=2)
      'args' => 
        array
          empty
  1 => 
    array
      'file' => string '**\index.php' (length=48)
      'line' => int 119
      'function' => string 'unknown' (length=7)
resource(26, Unknown)

如果我将测试更改为:

$sadb1 = manager::getInstance()->getStandaloneAdapter();
var_dump($sadb1->getConnection());

输出很好:

resource(26, mysql link)
destructor
array
  0 => 
    array
      'function' => string '__destruct' (length=10)
      'class' => string 'abstract_adapter' (length=16)
      'type' => string '->' (length=2)
      'args' => 
        array
          empty

wtf?!


第一次测试中运行的早期析构函数是自动垃圾收集的结果。为了理解这一点,让我们看看第二个(更简单的)测试:

1. $db = Mv_Core_Db_Manager::getInstance();
2. $sadb = $db->getStandaloneAdapter('bdm_bcb');
3. var_dump($sadb->getConnection());

Steps:

  1. 数据库管理器被分配给变量 $db,
  2. 独立适配器(在本例中是 MySQL 适配器,因为您在工厂中引入了用于调试的 hack)被分配给 $sadb,
  3. var_dump()调试的返回值getConnection()$sadb 独立适配器的方法,即resource(29, mysql link)第二个输出中的行,
  4. 清理时间; PHP 垃圾收集器运行 $sadb 的析构函数(由于调试,在输出中可见),然后 $db (在输出中不可见)。

这里最后发生垃圾收集。

如果您考虑您所描述的第一个测试,尽管源代码看起来非常相似,但它具有不同的步骤:

1. $db = Mv_Core_Db_Manager::getInstance();
2. $sadb = $db->getStandaloneAdapter('bdm_bcb')->getConnection();
3. var_dump($sadb);

Steps:

  1. 与上面的测试用例相同,
  2. 的返回值getConnection()MySQL独立适配器对象的getter被分配给$sadb,
  3. 因为MySQL独立适配器本身有not被分配给任何变量,PHP 垃圾收集器决定不再使用它,因此它会清理该对象并运行其析构函数(析构函数调试首先在输出中可见),
  4. var_dump()调试的返回值getConnection()MySQL 独立适配器的 getter,它基本上是已被垃圾收集器收集的资源的句柄。

这里垃圾收集发生在之前var_dump().

总而言之,您提供的第一个测试会强制垃圾收集器在代码的第 2 行和第 3 行之间跳转。另一方面,第二个测试强制在最后进行垃圾收集。

结果是您的资源句柄指向已被 GC 清理的内存。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

php 析构函数在流畅的界面下调用得太早 的相关文章

随机推荐

  • 如何使用事件处理程序检测实时 DigiScan 图像何时完成获取完整帧?

    我正在寻找实时 DigiScan 图像的快照 但是 我只想在获取完整 或接近完整 的帧时才这样做 将事件处理程序附加到实时 DigiScan 图像不起作用 因为图像会随着连续采集的每一行而 变化 我希望仅监视实时图像的最后几个像素 并仅在这
  • Wordpress 有条件 if is_single

    我尝试在我的 single php 页面上使用条件语句 我想要做的是 如果它是自定义帖子类型 current products 则使用特定的 single product php 模板页面 如果不是 即标准博客文章 则使用默认的 singl
  • 使用 LINQ to MySQL (DbLinq) 和动态 LINQ 的可排序 JqGrid - Orderby 不起作用

    我在 JqGrid 中排序条目时遇到问题 Orderby 似乎不起作用 我在代码中设置了断点 我注意到 orderby 不会改变元素的顺序 知道可能出什么问题吗 我正在使用 LINQ to SQL 和 MySQL DbLinq 项目 我的操
  • 如何使用 BERT 进行机器翻译?

    我遇到了一个大问题 对于我的学士论文 我必须使用 BERT 制作一个机器翻译模型 但我现在一事无成 您知道可以在这里帮助我的文档或其他东西吗 我已经阅读了一些这方面的论文 但也许有文档或教程可以帮助我 对于我的学士论文 我必须将文本摘要翻译
  • 如何让 JavaScript 延迟,然后刷新页面

    我希望我的 JavaScript 在我创建的函数结束时等待七秒钟 然后刷新我的页面 如果它很重要 我在下面列出了 JavaScript 和 HTML 的重要部分 JavaScript var textfill function var no
  • 录音在java中不工作

    我正在尝试通过java录制声音 该声音正在我的Windows机器上通过扬声器 耳机播放 我遇到的问题是我没有找到 AudioSystem 支持的单个 TargetDataLine 我尝试了 getSupportedFormats 方法来检查
  • 编程范式、设计模式和应用程序架构之间的区别?

    我没有编程背景 经常遇到编程范式 设计模式和应用程序架构等术语 虽然我认为我对这些术语的含义有一个模糊的理解 但如果有人能够澄清每个术语是什么 它们与其他术语有何不同以及这些概念如何应用于 Objective C 我将不胜感激 编程范式 类
  • 如何显示输入类型=“datetime-local”的值?

    我的代码是这样的 div test div div div
  • 如何确定 QAction 的来源?

    我想知道是否有任何简单 优雅的方法来确定 QAction 的来源 我的意思是我有一个 QAction 它被添加到多个 QWidget 带有QWidget addAction http doc qt nokia com latest qwid
  • 以编程方式清除cloudflare缓存

    在将请求发送到 node js api 后 我尝试以编程方式清除单个 url 的 cloudflare 缓存 我正在使用https github com cloudflare node cloudflare https github com
  • 尝试计算手机解锁的次数

    几个月前我对此感到厌倦 但失败了 我想做的是计算用户解锁手机的次数并将其显示在屏幕上 但每次解锁手机时我都会得到模糊的数字 我的代码如下 我的主要活动 oncreate Override protected void onCreate Bu
  • 将文本插入传入电子邮件 Outlook 2013 锁定只读

    我正在努力通过 VBA 在传入电子邮件中插入字符串 该例程在发送邮件时工作正常 并且如果用户单击 操作编辑 它将对传入邮件起作用 问题是传入邮件被锁定为只读模式 在过去的 13 5 个小时里我到处寻找 在早期版本的 Outlook 中这是可
  • 如何在具有多个峰值的数据集中找到 FWHM?

    我正在使用一个 Python 脚本 它可以在特定的值范围内沿着一维轮廓查找峰值 或在我的例子中为谷值 的索引 我的目标是测量每个感兴趣的山谷的半高宽 这是一维轮廓的示例 https i stack imgur com UTj33 png 这
  • 当应用程序在后台时,android O 中的 onMessageReceived 不会被调用

    我正在从我的服务器发送数据有效负载通知 这是示例 url https fcm googleapis com fcm send to userToken data some json here 通过这种方式 即使应用程序没有运行 我也可以在所
  • 内省和反思有什么区别?

    任何人都可以从语言 环境不可知论的角度解释这两个概念之间的区别吗 编程语言是否需要满足一组条件才能进行反思和 或内省 如果有的话 这些条件是什么 维基百科文章 http en wikipedia org wiki Type introspe
  • 更新错误“模块”对象不可调用后,pip 不再工作

    pip 更新后 pip 已完全停止工作 Z gt pip install matplotlib Traceback most recent call last File c program files python37 lib runpy
  • 在 U-Boot 中使用 I2C 读取多个字节

    我的 Freescale p1022tw 板的 I2C 驱动程序有问题 U Boot 的控制台上有一个从 I2C 设备读取的命令 i2c md chip address 0 1 2 of objects 当我从 id 为 0x60 地址为
  • 为什么使用 Object.create() 和 console.log() 时不显示属性?

    当我使用文字语法创建对象时 将对象打印到控制台会显示该对象foo和p财产 foo foo p 42 console log foo console log foo p Outputs p 42 当我使用 Object create 语法时
  • SWI序言中#=和=:=有什么区别

    What is the difference between and in SWI prolog I have found the definition from SWI prolog but still confused about it
  • php 析构函数在流畅的界面下调用得太早

    我发现 php 析构函数有一个非常奇怪的事情 基本上我有一个数据库管理类 它使用工厂加载适配器来定义应该加载哪个适配器 mysql mysqli等 我只会写下代码中有趣的部分 因为类本身更长 但代码不涉及当前的麻烦 该问题仅发生在 mysq