我可以信任调用 PHP __destruct() 方法吗?

Posted

技术标签:

【中文标题】我可以信任调用 PHP __destruct() 方法吗?【英文标题】:Can I trust PHP __destruct() method to be called? 【发布时间】:2010-09-14 04:17:54 【问题描述】:

php5 中,是否保证为每个对象实例调用 __destruct() 方法?程序中的异常可以防止这种情况发生吗?

【问题讨论】:

【参考方案1】:

还值得一提的是,在子类有自己的析构函数的情况下,父析构函数不会自动调用。

如果父类进行任何必要的清理,您必须从子类 __destruct() 方法中显式调用 parent::__destruct()

【讨论】:

我相信只有当子类实现自己的__destruct()时才会这样,否则会调用父类的__destruct()。【参考方案2】:

释放所有引用或脚本终止时将调用析构函数。我认为这意味着脚本正确终止时。我会说关键异常并不能保证调用析构函数。

PHP documentation 有点薄,但它确实表示析构函数中的异常会导致问题。

【讨论】:

我可以说,关机过程中的异常可能会导致分段错误。 此外,在已经位于 exit() 序列内的析构函数中调用 exit() 可以终止进程,而无需调用额外的析构函数。 对不起,我无法想象你的例子。你能详细说明一下吗? @JasonCohen 如果你需要在 php 致命错误的情况下调用析构函数,然后在构造函数中将其注册为关闭处理程序:public function __construct() register_shutdown_function(array($this, '__destruct')); 。这个解决方案的代价是对象引用(和对象本身)一直存在到 php 脚本执行结束。尽管如此,还是有一些情况是值得的 - 例如。在析构函数中删除巨大的 tmp 文件。【参考方案3】:

根据我的经验,在 PHP 5.3 中总是会调用析构函数,但请注意,如果某些代码调用 exit() 或发生致命错误,PHP 将以“任意”顺序调用析构函数(我认为实际顺序是内存中的顺序还是为对象保留内存的顺序。实际上,这个顺序几乎总是有问题的)。这在 PHP 文档中称为“关闭序列”。

PHP documentation of destructors 说:

PHP 5 引入了与其他面向对象语言(例如 C++)类似的析构函数概念。只要没有其他对特定对象的引用,或者在关闭序列期间以任何顺序调用,就会立即调用析构函数。

因此,如果您的类 X 包含对 Y 的引用,则 X 的析构函数可能会在 Y 的析构函数已经被调用之后被调用。希望对 Y 的引用不是那么重要……官方称这不是错误,因为它已被记录在案。

但是,很难解决这个问题,因为官方 PHP 无法知道是否正常调用析构函数(以正确的顺序调用析构函数)或以“任何”顺序调用析构函数,在这种情况下您不能使用来自引用对象的数据因为那些可能已经被摧毁了。可以使用 debug_backtrace() 并检查堆栈来解决这种检测不足的问题。缺少正常堆栈似乎意味着 PHP 5.3 的“关闭顺序”,但这也是未定义的。如果您有循环引用,那么这些对象的析构函数将不会在 PHP 5.2 或更低版本中被调用,并且将在 PHP 5.3 或更高版本的“关闭序列”期间以“任何”顺序调用。对于循环引用,不存在逻辑上“正确”的顺序,因此“任何”顺序都适用。

有一些例外(这毕竟是 PHP):

如果在另一个析构函数中调用exit(),则不会调用任何剩余的析构函数 (http://php.net/manual/en/language.oop5.decon.php) 如果FATAL 错误发生在任何地方(许多可能的原因,例如试图从任何其他析构函数中抛出异常可能是其中一个原因),则不会调用任何剩余的析构函数。

当然,如果 PHP 引擎遇到分段错误或发生其他内部错误,那么所有的赌注都没有了。

如果您想了解当前“关机序列”的实现,请参阅https://***.com/a/14101767。请注意,此实现可能会在未来的 PHP 版本中发生变化。

【讨论】:

PHP 的基本原理:“我坚持当前的实现,因为我认为这是最好的权衡。--Andi”。来源:mail-archive.com/internals@lists.php.net/msg06393.html 基本上有两组编码器:(1)在析构函数中使用全局变量的编码器和(2)在析构函数中不使用全局变量并通过使用对依赖对象的对象引用正确标记依赖关系的编码器.因为 PHP 试图以底端为目标,所以它满足 (1) over (2) 的需求。组 (2) 留下了工作不正确的代码,并且没有正式的解决问题的方法。 另见:***.com/questions/2385047/…【参考方案4】:

当前存在一个循环引用错误,该错误会阻止隐式调用 destruct 方法。 http://bugs.php.net/bug.php?id=33595 它应该在 5.3 中修复

【讨论】:

5.3关闭。【参考方案5】:

如果你想确定,请使用关机功能:register_shutdown_function()

【讨论】:

来自文档:“可以对 register_shutdown_function() 进行多次调用,并且每个调用都将按照它们注册时的相同顺序调用。如果在一个注册的关闭函数中调用 exit(),则处理将完全停止,并且不会调用其他已注册的关闭函数。”如果其他关闭函数在您之前运行,则无法保证 您的 关闭函数会被调用。【参考方案6】:

值得注意的是,虽然析构函数可能会被调用,但并不能保证它们会被调用,正如The Daily WTF 中的这个故事所示:

“好吧,”我叹了口气。尽管要求他描述刚刚捏造的流程编组概念很诱人,但我还是决定让步。就在那一刻,我想到了完美的反驳。 “但如果你只是,说,拔掉插头呢? Finally block __destruct 在计算机关闭时不会执行!”

我希望开发人员会因为“不合理的场景”而跳回去责备我。可他们的脸色却是一片白。他们慢慢转过头来看着对方。很明显,他们犯了与之前许多人一样的错误:相信 Try-Finally 与数据库事务之类的东西一样可靠。

“而且……嗯……”我慢慢地说,打破了尴尬的沉默,“这就是为什么……你应该……永远不要把关键的……业务交易代码放在 finally 块中。”

如果您只是释放资源或进行一些日志记录,可以假设将调用析构函数,但它们不能安全地用于“撤消”先前的操作以尝试使某些代码符合 ACID。

【讨论】:

以上是关于我可以信任调用 PHP __destruct() 方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

跟着百度学PHP[4]OOP面对对象编程-6-构造方法(__construct)和构析方法(__destruct)

PHP魔术方法使用总结

PHP OOP 魔术方法

PHP中的魔术方法:__construct, __destruct , __call,__get, __set, __isset, __unset , __toString, __set,__clon

PHP之析构方法学习笔记

php __destruct 为啥通过 file_get_contents 得到不同的结果