【问题标题】:How to catch the fatal error: Maximum execution time of 30 seconds exceeded in PHP如何捕获致命错误:PHP 中的最大执行时间超过了 30 秒
【发布时间】:2011-10-15 05:14:36
【问题描述】:

我一直在玩我正在开发的系统,并设法让它导致这个:

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

它发生在我做一些不切实际的事情时,但它可能发生在用户身上。

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

【问题讨论】:

  • 相信一旦超过执行时间,脚本就会终止。在这种情况下,可能已经捕获异常的脚本已经被杀死了。
  • 无法捕获或处理致命错误(它们不是异常)。您只能通过registering a shutdown function 优雅地处理脚本终止,但脚本将在之后结束。
  • 不,可能的重复不是“完全”重复。它不要求脚本超时引发的致命错误,而只要求通常如何捕获致命错误。但是由于在这种情况下一般也回答了具体问题,因此它有资格成为可能的重复项。如果问题是如何解决脚本超时错误,那么它可能与大多数 stackoverflow.com/search?q=maximum+execution+time+php 重复;)
  • 一般来说我无法回答这个问题,但在某些情况下,您可以将某些事情的时间限制为小于 max_execution_time,然后抓住它。例如 curl_setopt($client, CURLOPT_TIMEOUT, 27);将导致 curl 在 27 秒后放弃,这样您就不会遇到致命错误。

标签: php try-catch fatal-error


【解决方案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);
?>

查看以下链接:

  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

【讨论】:

  • 这可能是我从未见过的 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
        )
        

        参考资料:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-07-07
          • 2015-11-08
          • 2019-04-25
          • 2012-03-31
          • 2015-03-31
          相关资源
          最近更新 更多