【发布时间】:2019-07-26 18:16:39
【问题描述】:
我正在研究资源流,更具体地说,是在 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 堆栈帧)。
我还测试了时间是否重要,但即使每次迭代之间有 3 毫秒的睡眠,崩溃也会在或多或少相同数量的迭代后发生。这个数字在 700 左右,但波动非常小,有时会在 704 处正常运行,有时则不会。
此外,在 php 错误跟踪器上搜索并没有显示任何相关信息,除了可能是 bug entry,它谈到了对处理函数的调用存在处理这一事实。这可能意味着异常有可能绕过函数退出时的一些处理,但由于我对 PHP 源代码一无所知,这纯粹是猜测。
由于我想正确传播错误消息,使用 set_error_handler() 的方式将是最清晰的方式,但我知道我可以使用 error_get_last() 和 @ 运算符来实现相同的目标很多更多代码(因为在实际项目中,有多个函数如 fopen() 一个接一个地调用)。
所以这里有一些问题:这是 PHP 的错误吗?有没有办法在保持代码清晰的同时规避这个问题?
谢谢。
PS:我知道基准测试的原因是......充其量是可疑的,只要性能可行,我应该采用最清晰的代码,但它仍然让我发现了这个有趣的代码点。
编辑:我忘了放我测试过的版本:
- Windows 7、Apache 2.4.38、PHP 7.3.2(通过 XAMPP)
- Windows 7、Apache 2.4.29、PHP 7.2.2(通过 XAMPP)
- Ubuntu 服务器 18.04、Apache/2.4.29 (Ubuntu)、PHP 7.2.15-0ubuntu0.18.04.1
【问题讨论】:
标签: php apache exception crash php-7