43

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

4

6 回答 6

45

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

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

于 2008-09-30T03:53:49.917 回答
36

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

PHP 文档有点薄,但确实说析构函数中的异常会导致问题。

于 2008-09-30T03:35:23.157 回答
14

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

析构函数的 PHP 文档说:

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 版本中更改。

于 2011-11-28T09:39:56.100 回答
10

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

于 2008-09-30T04:01:08.870 回答
8

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

于 2011-12-30T23:10:32.500 回答
0

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

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

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

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

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

于 2021-12-18T19:28:36.390 回答