PHP:析构函数与 register_shutdown_function

Posted

技术标签:

【中文标题】PHP:析构函数与 register_shutdown_function【英文标题】:PHP: destructor vs register_shutdown_function 【发布时间】:2010-09-19 04:54:22 【问题描述】:

我有一个 php 类,可以即时创建 PNG 图像并将其发送到浏览器。 PHP 手册说我需要确保最后调用 imagedestroy 函数来释放内存。现在,如果我不使用类,我会有一些这样的代码:

function shutdown_func() 

    global $img;
    if ($img)
        imagedestroy($img);

register_shutdown_function("shutdown_func");

但是,我认为适合我的班级的地方是在班级的析构函数中调用 imagedestroy

我不知道是否像关闭函数一样调用析构函数?例如,如果用户在浏览器中按下 STOP 按钮时执行停止。

注意:无论您在回答中写什么,请指向一些支持它的文章或手册页(URL)。

【问题讨论】:

如果您不编辑我的答案以删除您认为“不相关”的部分,我将不胜感激,这是不尊重的。关于垃圾收集,它是相关的,但适合你自己。 @Tomalak:你的答案的另一部分是相关的和现场的,我什至投了赞成票。你能把它放回去吗? 【参考方案1】:

我认为您错过的一件大事是,一旦脚本终止,PHP 在脚本执行期间分配的所有内存都会被释放。即使用户按下停止按钮,PHP 也会处理脚本直到完成,然后将其返回给 HTTP 守护程序以提供给访问者(或不提供,取决于守护程序的聪明程度)。

因此,在脚本执行结束时显式释放内存有点多余。有些人可能会争辩说这样做是件好事,但它仍然是多余的。

但是,关于类析构函数,只要对象被销毁,它们就会被调用,无论是由unset() 明确地还是在脚本完成/终止时。

开发人员建议明确释放图像处理中使用的内存只是为了确保绝对不会发生内存泄漏,因为位图可能会占用内存方面的东西(高度 * 宽度 * 位深度 * 3(如果有 Alpha 通道,则为 +1))

为了满足您的***需求:

http://php.net/manual/en/language.oop5.decon.php http://php.net/manual/en/function.unset.php

【讨论】:

AFAIU,内存没有被 PHP 分配,因为它调用 GD 创建图像并且 GD 没有垃圾回收。 我已经阅读了PHP手册,但没有明确说明如果脚本被Web服务器终止,是否调用析构函数。 也是AFAIK,GD只是一个与PHP动态链接的库。这意味着虽然 GD 知道如何做所有事情,但它仍然是 PHP 的内存,因此是 PHP 的垃圾收集。 @wolfie:没有。我刚刚测试过,它会泄漏内存。由于 PHP 是 Apache 模块,它会在 Apache 重新启动时返回到操作系统,我不经常这样做(任何托管公司都试图根本不重新启动 Web 服务器) 根据是模块还是 cgi/fast-cgi 设置,情况可能会有所不同。【参考方案2】:

我刚刚用 Apache 进行了测试,PHP 被用作 Apache 模块。我创建了一个这样的无限循环:

<?php
class X

    function __destruct()
    
        $fp = fopen("/var/www/htdocs/dtor.txt", "w+");
        fputs($fp, "Destroyed\n");
        fclose($fp);
    
;

$obj = new X();
while (true) 
    // do nothing

?>

这是我发现的:

在 Firefox 中按 STOP 按钮不会停止此脚本 如果我关闭 Apache,析构函数不会被调用 当它到达 PHP max_execution_time 并且没有调用析构函数时它会停止

但是,这样做:

<?php
function shutdown_func() 
    $fp = fopen("/var/www/htdocs/dtor.txt", "w+");
    fputs($fp, "Destroyed2\n");
    fclose($fp);

register_shutdown_function("shutdown_func");

while (true) 
    // do nothing

?>

shutdown_func 被调用。所以这意味着类析构函数不如关闭函数好。

【讨论】:

您能否在 php 手册或其他支持您的测试的内容中添加链接?这对我们大家都大有裨益。 如果你有一个工作设置,只需复制/粘贴我给出的这个例子并自己尝试。它在 PHP 手册中有解释,否则我不会在这里问。 这种行为不是“设计使然”,因此它可能会在未来的版本中发生变化。如果没有输出发送到浏览器,则停止按钮无效。 “echo”是一个可能导致致命错误(损坏的管道)的函数。感谢您的研究,但在 GD img 案例中,每个人都应该使用析构函数方法。 PHP 手册中记录了这一点。当脚本结束时,注册的关闭函数和析构函数都会被调用。但是,如果连接状态更改为 ABORTED(php 检测到用户按下了浏览器停止按钮)并且 ignore_user_abort 未设置为 true,则注册的关闭函数将不会在脚本结束时调用,而是在检测到时调用。见Shutdown/Destructor 和connection handling。 事实上,在 Firefox(客户端)中按下 Stop 并不会停止执行您的脚本(服务器端),这对我来说非常明显,我一定错过了一些要点。你不可能假设这可以这样工作,对吧?与关闭 Apache(杀死一切)并达到max_execution_time(危急情况,杀死一切)相同。以上帝的名义,您怎么能期望在任何这些情况下 PHP 都能够调用您的析构函数?如果它在这种情况下立即被完全彻底杀死。我一定在你的回答中遗漏了什么!【参考方案3】:

基于你应该finish what you start的原则,我会说析构函数是免费调用的正确位置。

destructor 将在对象被释放时被调用,而shutdown function 在脚本执行完成之前不会被调用。正如 Wolfie 所说,如果你强制停止服务器或脚本,这些情况不一定会发生,但到那时,PHP 分配的内存无论如何都会被释放。

Wolfie 还指出,PHP 将在脚本关闭时释放脚本资源,因此如果您只是实例化这些对象之一,那么您可能不会注意到有很大的不同。但是,如果您稍后确实实例化了这些东西,或者在循环中这样做,那么您可能不想担心内存使用量突然飙升,所以为了将来的理智,我回到我的原始推荐;将其放入析构函数中。

【讨论】:

【参考方案4】:

我最近遇到了这个问题,因为我试图专门针对服务器超时的情况处理破坏,并且我想在错误日志中包含类数据。引用 &$this 时我会收到一个错误(尽管我在几个例子中看到过,可能是版本问题或 symfony 副作用),我想出的解决方案相当干净:

class MyClass

    protected $myVar;

    /**
     * constructor, registers shutdown handling
     */
    public function __construct()
    
        $this->myVar = array();

        // workaround: set $self because $this fails
        $self = $this;
        // register for error logging in case of timeout
        $shutdown = function () use (&$self) 
            $self->shutdown();
        ;
        register_shutdown_function($shutdown);
    

    /**
     * handle shutdown events
     */
    public function shutdown()
    
        $error = error_get_last();
        // if shutdown in error
        if ($error['type'] === E_ERROR) 
            // write contents to error log
            error_log('MyClass->myVar on shutdown' . json_encode($this->myVar), 0);
        
    

    ...

希望这对某人有所帮助!

【讨论】:

以上是关于PHP:析构函数与 register_shutdown_function的主要内容,如果未能解决你的问题,请参考以下文章

PHP:析构函数与 register_shutdown_function

php构造函数的PHP 5 构造函数和析构函数

PHP面向对象--构造函数与析构函数

php面向对象析构函数实践示例(数据库连接与释放)

php利用php的构造函数与析构函数编写Mysql数据库查询类 (转)

关于PHP面向对象中—类的定义与对象的实例化操作以及构造析构函数的特殊用法