我正在研究资源流,尤其是在fopen()上。 除了返回false而不是资源之外,此函数在失败时还会引发警告。我决定抑制不必要的警告,这是一个问题。
我想到了两种可能性:使用错误抑制运算符@或使用set_error_handler()。并被告知@性能不佳,并且所提出的问题往往比解决的问题多,我运行了一个快速基准测试,以查看set_error_handler()与之相违背。
下面是有问题的代码:
<?php
error_reporting(E_ALL);
function errorHandler(int $errorNumber, string $errorMessage)
{
throw new \Exception();
}
$previousHandler = set_error_handler("errorHandler");
$operations = 10000;
for($i = 0; $i < $operations; $i++) {
try{
$inexistant[0];
} catch (\Exception $e) {}
}
set_error_handler($previousHandler);
echo 'ok';
运行以下简单代码将使Apache服务器崩溃,并显示以下消息:
[mpm_winnt:notice] [pid 6000:tid 244] AH00428: Parent: child process 3904 exited with status 3221225725 -- Restarting.
搜索后,此消息表示服务器出现访问冲突错误,主要是在达到堆栈大小限制的情况下。但是,这不是事实,因为此代码不应增加堆栈大小(实际上,它不会增加PHP堆栈框架)。
我还测试了时间是否重要,但是即使每次迭代之间有3ms的睡眠时间,崩溃也会在大约相同数量的迭代之后发生。这个数字约为700,但波动很小,有时在704处运行良好,有时则没有。
此外,在php bug跟踪器上进行搜索并没有显示任何相关内容,除了可能bug entry之外,该内容讨论了对处理函数调用的处理。这可能意味着异常可能会绕过函数退出时的某些处理,但是由于我对PHP源代码一无所知,所以这纯粹是猜测。
由于我想正确传播错误消息,因此使用set_error_handler()的方法将是最清晰的方法,但是我知道我可以使用error_get_last()和@运算符来实现相同的目标更多的代码(因为像fopen()这样的多个函数在实际的projet中一个接一个地调用)。
因此,这里有个问题:这是PHP的错误吗?有没有办法在保持清晰代码的同时规避这个问题?
谢谢。
PS:我知道基准测试的理由充其量是可疑的,只要性能可行,我应该采用最清晰的代码,但这仍然让我发现了代码的这一有趣之处。 / p>
编辑:我忘了将我对此进行测试的版本放在上面:
答案 0 :(得分:0)
为什么要经历所有这些麻烦?仅处理false
的返回值会好得多,因为docs声明:Returns a file pointer resource on success, or FALSE on error.
因此,仅检查fopen
的返回值是否为false以及然后继续基于此操作(或在必要时抛出您自己的错误)。
答案 1 :(得分:0)
由于已确认这是一个PHP错误,并且已找到确切原因,因此我将同时发布答案和解决方法,直到错误被解决为止。
首先,这里是错误报告:https://bugs.php.net/bug.php?id=77693。
这是导致捕获异常上下文的堆栈溢出,在这种情况下,该异常包括调用错误处理程序的函数,因为它的行为是捕获父上下文。此父上下文包括previoulsy捕获的异常(已添加到要捕获的新异常的上下文中),并重复ad vitam aeternam直到崩溃。
由于已明确确定原因,解决方案很简单:只需在catch块的末尾添加unset()即可,
$operations = 10000;
for($i = 0; $i < $operations; $i++) {
try{
$inexistant[0];
} catch (\Exception $e) {
unset($e);
}
}
然后就没有问题了。
要添加到第二个问题中,要求在使用fopen的情况下使用干净的替代品以及类似的其他方法,这是一个解决方案:
function throwLastError() {
$context = error_get_last();
error_clear_last();
throw new ErrorException($context["message"],
0,
$context["type"],
$context["file"],
$context["line"]);
}
// Wrong call to fopen
if (!@fopen("", "a"))
throwLastError();
当两个错误参数都使用时,两个答案的性能大致相同,@方法要慢10%。