如何捕获致命错误:PHP 中的最大执行时间超过了 30 秒
Posted
技术标签:
【中文标题】如何捕获致命错误:PHP 中的最大执行时间超过了 30 秒【英文标题】:How to catch the fatal error: Maximum execution time of 30 seconds exceeded in PHP 【发布时间】:2011-10-15 05:14:36 【问题描述】:我一直在玩我正在开发的系统,并设法让它导致这个:
致命错误:超过 30 秒的最大执行时间
它发生在我做一些不切实际的事情时,但它可能发生在用户身上。
有谁知道是否有办法捕捉这个异常?我已经阅读过,但似乎每个人都建议增加允许的时间。
【问题讨论】:
相信一旦超过执行时间,脚本就会终止。在这种情况下,可能已经捕获异常的脚本已经被杀死了。 无法捕获或处理致命错误(它们不是异常)。您只能通过registering a shutdown function 优雅地处理脚本终止,但脚本将在之后结束。 How do I catch a php Fatal Error的可能重复 不,可能的重复不是“完全”重复。它不要求脚本超时引发的致命错误,而只要求通常如何捕获致命错误。但是由于在这种情况下一般也回答了具体问题,因此它有资格成为可能的重复项。如果问题是如何解决脚本超时错误,那么它可能与大多数 ***.com/search?q=maximum+execution+time+php 重复;) 一般来说我无法回答这个问题,但在某些情况下,您可以将某些事情的时间限制为小于 max_execution_time,然后抓住它。例如 curl_setopt($client, CURLOPT_TIMEOUT, 27);将导致 curl 在 27 秒后放弃,这样您就不会遇到致命错误。 【参考方案1】:试试 PHP 文档怎么样(嗯……至少它的一位读者)说:
<?php
function shutdown()
$a = error_get_last();
if ($a == null) echo "No errors";
else print_r($a);
register_shutdown_function('shutdown');
ini_set('max_execution_time', 1);
sleep(3);
?>
查看以下链接:
-
http://www.php.net/manual/en/function.set-error-handler.php#106061
http://www.php.net/manual/en/function.register-shutdown-function.php
【讨论】:
这可能是我从未见过的 php 最保守的秘密。太棒了。 我测试过了。但仍然显示致命错误消息。能不能避免。我使用的任何方式 ini_set('display_errors', '0'); sleep() 实际上并不影响时间限制计数器,这很奇怪,但确实如此。所以你应该做一些其他的事情来测试你的 shutdown() 函数——几个嵌套的 for 循环,每个循环迭代一百万次。 @BobbyJack 虽然这很奇怪,但这是有道理的,因为休眠线程会停止执行一段时间。因为它没有执行,所以那个时间不被认为是经过的执行时间。 确实是个不错的选择。就我而言,通过使用此功能,我可以使用 javascript 作为最终资源将用户重定向到另一个页面/服务器。【参考方案2】:您唯一的选择是增加脚本的允许执行时间(将其设置为 0 使其无限期,但不建议这样做)或生成一个新线程并希望最好。
无法捕获的原因是它并没有真正被抛出。没有一行代码实际上触发了错误,而是 PHP 说,“不,抱歉,这太长了。现在该关闭了。”这是有道理的。想象一下,有一个最大执行时间为 30 秒的脚本捕获该错误并再花费 30 秒......在一个设计不佳的程序中,这会带来一些相当讨厌的利用机会。至少,它会为DOS 攻击创造机会。
【讨论】:
如何将异常时间设置为 0,然后自己做超时? 这在某些情况下可能有效,但不是全部。如果它是某种形式的循环可能是最好的,这样您就可以定期测试。 嗯。我注意到它的原因是在接收数据包块时。我将数据包大小设置为非常小的东西,并且它需要很长时间才能发送。我的想法是,如果要发送大量数据,数据包大小约为 1024 字节,我可以在该循环上设置自己的超时,并在几秒钟内做出反应以警告用户。 我反对被 PHP 告知编码缺陷是否允许无限循环或 DOS 漏洞利用等。我认为应该由程序员来处理错误。也许超时发生在 cURL 期间,我希望页面无限刷新,直到它重新连接到 url(因为 T1 线路已关闭,或者可能是远程服务器,谁知道?)。我不希望我的预处理器(无论是 php、asp、cgi 等)为我做出决定。我认为预处理器,甚至像 javascript 这样的客户端语言不应该代表我做出道德或道德决定。 您可以采取与超时相关的其他选项,但本问题未涵盖这些选项。最常见的做法是简单地确保请求不超过 60 秒。到那时,你无论如何都不会公开面对。【参考方案3】:这不是一个例外,这是一个错误。异常和错误之间有重要的区别,首先是错误不能用 try/catch 语义捕获。
PHP 脚本是围绕短执行时间的范例构建的,因此 PHP 默认配置为假设如果脚本已运行超过 30 秒,则它必须陷入无限循环,因此应终止。这是为了防止错误的 PHP 脚本意外或恶意导致拒绝服务。
但是,脚本有时确实需要比默认分配更多的运行时间。
您可以尝试更改最大执行时间,方法是使用 set_time_limit()
或更改 php.ini
文件中 max_execution_time
的值以提高限制。您也可以通过将执行时间设置为 0 来完全取消限制,但不建议这样做。
set_time_limit()
可能会被disable_functions
等机制禁用,因此您可能无法使用它,同样您可能无权访问php.ini
。如果这两种情况都存在,那么您应该联系您的房东寻求帮助。
一个例外是 PHP 脚本从 命令行 运行。在这些运行条件下,PHP 脚本可能是交互式的,需要花费很长时间处理数据或等待输入。因此,默认情况下从命令行运行的脚本没有max_execution_time
限制。
编辑添加:PHP 7 的错误处理进行了大修。我相信错误和异常现在都是 Throwable 的子类。这可能会使上述内容与 PHP7+ 不再相关,但我必须更仔细地研究错误处理的具体细节才能确定。
【讨论】:
【参考方案4】:对此你无能为力。但是您可以使用 register_shutdown_function 进行正常关机
<?php
ini_set('display_errors', '0');
ini_set("max_execution_time",15 ); //you can use this if you know your script should not take longer than 15 seconds to finish
register_shutdown_function('shutdown');
function shutdown()
$error = error_get_last();
if ($error['type'] === E_ERROR)
//do your shutdown stuff here
//be care full do not call any other function from within shutdown function
//as php may not wait until that function finishes
//its a strange behavior. During testing I realized that if function is called
//from here that function may or may not finish and code below that function
//call may or may not get executed. every time I had a different result.
// e.g.
other_function();
//code below this function may not get executed
while(true)
function other_function()
//code in this function may not get executed if this function
//called from shutdown function
?>
【讨论】:
如果在无限循环之前声明other_function()
,会在关机期间被调用吗?
@Douglas.Sesar other_function() 将以任何方式调用。但是不能保证它会完成并且函数调用下面的任何代码。如果 other_function() 花费很少的时间来做很少的事情,它可能会完成并且函数调用下面的任何代码。但是如果完成任务花费的时间太多,则无法保证它会完全完成!
在 OP 问题的最严格意义上,此解决方案有效。我想感谢你的回答让我在春季登机时得到了稍微详细的回答。【参考方案5】:
是的,我测试了 TheJanOnline 的解决方案。 sleep() 不计入 php 执行时间,所以这里是无限循环的 WORKING 版本:
<?php
function shutdown()
$a=error_get_last();
if($a==null)
echo "No errors";
else
print_r($a);
register_shutdown_function('shutdown');
ini_set('max_execution_time',1 );
while(1) /*nothing*/
// will die after 1 sec and print error
?>
【讨论】:
【参考方案6】:在某些情况下,将“致命错误:超过 30 秒的最大执行时间”作为异常处理有一些棘手的方法:
function time_sublimit($k = 0.8)
$limit = ini_get('max_execution_time'); // changes even when you set_time_limit()
$sub_limit = round($limit * $k);
if($sub_limit === 0)
$sub_limit = INF;
return $sub_limit;
在您的代码中,您必须测量执行时间并在可能触发超时致命错误之前抛出异常。 $k = 0.8 是允许执行时间的 80%,因此您有 20% 的时间来处理异常。
try
$t1 = time(); // start to mesure time.
while (true) // put your long-time loop condition here
time_spent = time() - $t1;
if(time_spent >= time_sublimit())
throw new Exception('Time sublimit reached');
// do work here
catch(Exception $e)
// catch exception here
【讨论】:
如果您的工作被很好地分割以便可以迭代,那么这将起作用,但是如果一个大型操作或操作序列一直持续到时间限制,您仍然在很大程度上索尔。【参考方案7】:我根据@pinkal-vansia 给出的答案提出了这个问题。所以我不是在声称一个原始答案,而是一个具有实际应用的答案。我需要一种方法让页面在超时时自行刷新。由于我一直在观察我的 cURL 脚本的足够超时以知道代码正在运行,但是有时由于某种原因它无法连接到远程服务器,或者无法完全读取提供的 html,并且在刷新后问题就消失了,我我可以通过脚本刷新自身来“治愈”最大执行超时错误。
<?php //script name: scrape_script.php
ini_set('max_execution_time', 300);
register_shutdown_function('shutdown');
function shutdown()
?><meta http-equiv="refresh" content="0; url=scrape_script.php"><?php
// just do a meta refresh. Haven't tested with header location, but
// this works fine.
仅供参考,对于我正在运行的抓取脚本来说,300 秒并不算太长,这比从我正在抓取的各种页面中提取数据所需的时间要少一点。有时,仅由于连接不规则,它只会持续几秒钟。知道有时失败的是连接时间,而不是脚本处理,最好不要增加超时,而只是自动刷新页面并重试。
【讨论】:
【参考方案8】:我遇到了类似的问题,我是这样解决的:
<?php
function shutdown()
if (!is_null($error = error_get_last()))
if (strpos($error['message'], 'Maximum execution time') === false)
echo 'Other error: ' . print_r($error, true);
else
echo "Timeout!\n";
ini_set('display_errors', 0);
register_shutdown_function('shutdown');
set_time_limit(1);
echo "Starting...\n";
$i = 0;
while (++$i < 100000001)
if ($i % 100000 == 0)
echo ($i / 100000), "\n";
echo "done.\n";
?>
这个脚本将在最后打印Timeout!
。
您可以将$i = 0;
这一行修改为$i = 1 / 0;
,它将打印出来:
Other error: Array
(
[type] => 2
[message] => Division by zero
[file] => /home/user/test.php
[line] => 17
)
参考资料:
PHP: register_shutdown_function - Manual PHP: set_time_limit - Manual PHP: error_get_last - Manual【讨论】:
以上是关于如何捕获致命错误:PHP 中的最大执行时间超过了 30 秒的主要内容,如果未能解决你的问题,请参考以下文章
致命错误:第 1610 行的 C:\xampp\htdocs\wordpress\wp-includes\class-http.php 中的最大执行时间超过 30 秒
PHP 页面加载问题:PHP 致命错误:超过 30 秒的最大执行时间
没有解决方案适用于我的 wamp 服务器错误:致命错误:第 239 行的 D\...\class-wp-http-curl.php 中的最大执行时间超过 60 秒
致命错误:第 463 行的 C:\xampp\htdocs\AKS_Shop\wp-includes\Requests\Transport\cURL.php 中的最大执行时间超过 30 秒