【发布时间】:2013-01-19 05:40:48
【问题描述】:
谁能告诉我在 PHP 中看到此错误的最常见原因:
无法在...中销毁活动的 lambda 函数
我猜想某处有代码试图破坏包含对自身的引用的闭包,编译器对此感到恼火。
我们比我想要的更频繁地获得这些,我想知道我们使用的是什么模式,这可能是导致它的罪魁祸首。
我会附加一个代码 sn-p,但错误通常指向一个文件,而不是文件中可能提供提示的行。
【问题讨论】:
谁能告诉我在 PHP 中看到此错误的最常见原因:
无法在...中销毁活动的 lambda 函数
我猜想某处有代码试图破坏包含对自身的引用的闭包,编译器对此感到恼火。
我们比我想要的更频繁地获得这些,我想知道我们使用的是什么模式,这可能是导致它的罪魁祸首。
我会附加一个代码 sn-p,但错误通常指向一个文件,而不是文件中可能提供提示的行。
【问题讨论】:
【讨论】:
这样的致命错误会导致如下代码:
set_exception_handler(function ($e) {
echo "My exception handler";
restore_exception_handler();
throw new Exception("Throw this instead.");
});
throw new Exception("An exception");
或者像这样:
function destroy_handler() {
restore_exception_handler();
}
function update_handler() {
destroy_handler();
}
set_exception_handler(function ($e) {
echo "My exception handler";
update_handler();
throw new Exception("Throw this instead.");
});
throw new Exception("An exception");
当抛出异常时,将执行给set_exception_handler() 的回调,并且一旦调用restore_exception_handler(),就会发生致命错误,因为对该闭包的相同引用正在其自己的范围内被销毁(或重新分配) (Sameer K 发布的链接中的hanskrentel at yahoo dot de 示例也是如此)。
您可以从第二个示例中看到,即使使用嵌套范围,也会发生同样的情况。这是因为restore_exception_handler 将销毁最后设置的异常处理程序,而不是它的副本(这就像在您仍在评估将为变量赋予初始值的表达式时重新分配变量或取消设置)。
如果您说代码中的错误指向另一个文件,我建议您检查 lambda 中的所有调用,这些调用在其他文件中的函数和/或方法中执行“跳转”,并检查您是否正在重新分配或销毁对 lambda 本身的引用。
【讨论】:
set_exception_handler 将返回之前的异常处理程序:
返回先前定义的异常处理程序的名称,或返回 NULL 错误。如果没有定义先前的处理程序,则也返回 NULL。 php.net: set_exception_handler
<?php
class MyException extends Exception {}
set_exception_handler(function(Exception $e){
echo "Old handler:".$e->getMessage();
});
$lastHandler = set_exception_handler(function(Exception $e) use (&$lastHandler) {
if ($e instanceof MyException) {
echo "New handler:".$e->getMessage();
return;
}
if (is_callable($lastHandler)) {
return call_user_func_array($lastHandler, [$e]);
}
throw $e;
});
触发异常处理程序:
throw new MyException("Exception one", 1);
输出:New handler:Exception one
throw new Exception("Exception two", 1);
输出:Old handler:Exception two
【讨论】: