如何捕获致命错误: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 秒的主要内容,如果未能解决你的问题,请参考以下文章

致命错误:超过 30 秒的最大执行时间 [重复]

致命错误:第 1610 行的 C:\xampp\htdocs\wordpress\wp-includes\class-http.php 中的最大执行时间超过 30 秒

致命错误:超过 400 秒的最大执行时间

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 秒