如何捕捉致命错误: PHP 中的最大执行时间超过30秒

我一直在开发一个系统,设法让它产生这个:

致命错误: 最长执行时间超过30秒

它发生在我做一些不现实的事情的时候,但是它仍然可以发生在用户身上。

有人知道是否有办法捕捉这个异常吗?我已经看过了,但是每个人似乎都建议增加允许的时间。

84555 次浏览

您唯一的选择是增加脚本的允许执行时间(将其设置为0使其无限,但不推荐这样做) ,或者产生一个新线程,然后期待最好的结果。

之所以抓不到它,是因为它并没有真正被扔出去。实际上没有一行代码触发这个错误,而是 PHP 说,“不,对不起,这太长了。是时候关门了”这就说得通了。想象一下,在一个设计糟糕的程序中,有一个最大执行时间为30秒的脚本捕捉到这个错误,然后又花费30秒... ... 这就开启了一些相当糟糕的利用机会。至少,它将为 DOS攻击创造机会。

这不是例外,这是个错误。异常和错误之间有着重要的区别,首先和最重要的错误不能用 try/catch 语义来捕获。

PHP 脚本是围绕短执行时间的范例构建的,因此 PHP 默认配置为假定如果脚本运行时间超过30秒,则必须在无限循环中捕获该脚本,因此应该终止。这是为了防止错误的 PHP 脚本引起分布式拒绝服务攻击,无论是偶然的还是恶意的。

但是,脚本有时需要比默认分配的运行时间更长的时间。

您可以尝试更改最大执行时间,可以使用 译自: 美国《科学》杂志网站(http://php.net/Manual/en/Function.set-time-lim.php),也可以通过改变 php.ini文件中 < a href = “ http://www.php.net/Manual/en/info.configation.php # ini.max-execute-time”rel = “ noReferrer”> max_execution_time 的值来提高限制。您也可以通过将执行时间设置为0来完全消除这个限制,但是不推荐这样做。

set_time_limit() 可能被诸如 disable_functions之类的机制禁用,因此它可能对您不可用,同样地,您也可能无法访问 php.ini。如果这两种情况都是如此,那么您应该联系您的主机寻求帮助。

一个例外是从 命令行运行的 PHP 脚本。在这些运行条件下,PHP 脚本可能是交互式的,需要花费很长时间处理数据或等待输入。由于这个原因,默认情况下对从命令行运行的脚本没有 max_execution_time限制。


编辑添加 : PHP 7的错误处理进行了重大修改。我相信错误和异常现在都是 Throwable 的子类。这可能使上面的内容不再适用于 PHP7 + ,尽管现在我必须更仔细地研究错误处理的具体工作方式以确保这一点。

不如试试作为 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);
?>

你可浏览以下连结:

  1. Http://www.php.net/manual/en/function.set-error-handler.php#106061
  2. Http://www.php.net/manual/en/function.register-shutdown-function.php

您对此无能为力,但是您可以使用 register _ close _ 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
}


?>

在某些情况下,处理“致命错误: 超过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
}

这是我根据“ 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秒并不太长,只需要比这短一点点的时间就可以从我正在抓取的各种页面中提取数据。有时候它只会因为连接不规则而延长几秒钟。知道有时会失败的是连接时间,而不是脚本处理,所以最好不要增加超时时间,而只是自动刷新页面并重试。

是的,我通过 The JanOnline 测试了解决方案。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
?>

我也遇到过类似的问题,我是这样解决的:

<?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
)

参考文献: