【发布时间】:2010-09-14 04:17:54
【问题描述】:
在 PHP5 中,是否保证为每个对象实例调用 __destruct() 方法?程序中的异常可以防止这种情况发生吗?
【问题讨论】:
在 PHP5 中,是否保证为每个对象实例调用 __destruct() 方法?程序中的异常可以防止这种情况发生吗?
【问题讨论】:
还值得一提的是,在子类有自己的析构函数的情况下,父析构函数不会自动调用。
如果父类进行任何必要的清理,您必须从子类 __destruct() 方法中显式调用 parent::__destruct()。
【讨论】:
释放所有引用或脚本终止时将调用析构函数。我认为这意味着脚本正确终止时。我会说关键异常并不能保证调用析构函数。
PHP documentation 有点薄,但它确实表示析构函数中的异常会导致问题。
【讨论】:
public function __construct() { register_shutdown_function(array($this, '__destruct')); }。这个解决方案的代价是对象引用(和对象本身)一直存在到 php 脚本执行结束。尽管如此,还是有一些情况是值得的 - 例如。在析构函数中删除巨大的 tmp 文件。
根据我的经验,在 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://stackoverflow.com/a/14101767。请注意,此实现可能会在未来的 PHP 版本中发生变化。
【讨论】:
当前存在一个循环引用错误,该错误会阻止隐式调用 destruct 方法。 http://bugs.php.net/bug.php?id=33595 它应该在 5.3 中修复
【讨论】:
如果你想确定,请使用关机功能:register_shutdown_function()
【讨论】:
值得注意的是,虽然析构函数可能会被调用,但并不能保证它们会被调用,正如The Daily WTF 中的这个故事所示:
“好吧,”我叹了口气。尽管要求他描述刚刚捏造的流程编组概念很诱人,但我还是决定让步。就在那一刻,我想到了完美的反驳。 “但如果你只是,说,拔掉插头呢?
Finally block__destruct 在计算机关闭时不会执行!”我希望开发人员会因为“不合理的场景”而跳回去责备我。可他们的脸色却是一片白。他们慢慢转过头来看着对方。很明显,他们犯了与之前许多人一样的错误:相信 Try-Finally 与数据库事务之类的东西一样可靠。
“而且……嗯……”我慢慢地说,打破了尴尬的沉默,“这就是为什么……你应该……永远不要把关键的……业务交易代码放在 finally 块中。”
如果您只是释放资源或进行一些日志记录,可以假设将调用析构函数,但它们不能安全地用于“撤消”先前的操作以尝试使某些代码符合 ACID。
【讨论】: