如何捕获致命错误:PHP 超出最大执行时间 30 秒

发布于 2024-11-26 22:52:29 字数 189 浏览 1 评论 0 原文

我一直在研究我正在开发的系统并设法让它导致这种情况:

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

当我做一些不切实际的事情时会发生这种情况,但尽管如此,用户也可能会发生这种情况。

有谁知道是否有办法捕获这个异常?我读过周围的资料,但似乎每个人都建议增加允许的时间。

I've been playing around with a system I'm developing and managed to get it to cause this:

Fatal error: Maximum execution time of 30 seconds exceeded

It happened when I was doing something unrealistic, but nevertheless it could happen with a user.

Does anyone know if there is a way to catch this exception? I've read around but everyone seems to suggest upping the time allowed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

零度℉ 2024-12-03 22:52:29

尝试一下 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

How about trying as PHP documentation (well... at least one of its readers) say:

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

Have a look at the following links:

  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
望喜 2024-12-03 22:52:29

您唯一的选择是增加脚本允许的执行时间(将其设置为 0 使其无限,但不建议这样做)或生成一个新线程并希望得到最好的结果。

这是不可捕获的原因是它没有真正被抛出。没有一行代码实际上触发了错误,而是 PHP 说:“不,抱歉,这太长了。现在该关闭了。”这是有道理的。想象一下,有一个最长执行时间为 30 秒的脚本捕获该错误,然后再花费 30 秒……在设计不良的程序中,这会带来一些相当令人讨厌的利用机会。至少,它会为 DOS 攻击创造机会。

Your only options are to increase the allowed execution time (setting it to 0 makes it infinite, but that is not recommended) of the script or spawn a new thread and hope for the best.

The reason that this isn't catchable is that it isn't really thrown. No one line of the code actually triggered the error, rather PHP said, "Nope, sorry, this is too long. Time to shut down now." And that makes sense. Imagine having a script with a max execution time of 30 seconds catching that error and taking another 30 seconds... in a poorly designed program, that opens up some rather nasty opportunities to exploit. At a minimum, it will create opportunities for DOS attacks.

云裳 2024-12-03 22:52:29

这不是异常,而是错误。异常和错误之间存在重要区别,首先也是最重要的错误不能用 try/catch 语义捕获。

PHP 脚本是围绕短执行时间的范例构建的,因此 PHP 默认配置为假设如果脚本运行时间超过 30 秒,则必须陷入无限循环,因此应终止。这是为了防止错误的 PHP 脚本意外或恶意地导致拒绝服务。

然而,脚本有时确实需要比默认分配的更多的运行时间。

您可以尝试更改最大执行时间,方法是使用 set_time_limit() 或通过更改 max_execution_timephp.ini 文件中提高限制。您还可以通过将执行时间设置为 0 来完全删除限制,但不建议这样做。

set_time_limit() 可以通过诸如 disable_functions 所以它您可能无法使用,同样您可能无法访问 php.ini。如果这两种情况都存在,那么您应该联系您的房东寻求帮助。

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


编辑添加:PHP 7 的错误处理进行了重大修改。我相信错误和异常现在都是 Throwable 的子类。这可能使上述内容不再与 PHP7+ 相关,尽管我必须更仔细地研究错误处理现在如何工作的细节才能确定。

This isn't an exception, it's an error. There are important differences between exceptions and errors, first and foremost errors can't be caught with try/catch semantics.

PHP scripts are built around a paradigm of short execution times, so PHP is configured by default to assume that if a script has been running for longer than 30 seconds it must be caught in an infinite loop and therefore should be terminated. This is to prevent an errant PHP script causing a denial of service, either by accident or by malicious intent.

However, scripts do sometimes need more running time than they are allocated by default.

You can try changing the maximum execution time, either by using set_time_limit() or by altering the value of max_execution_time in the php.ini file to raise the limit. you can also remove the limit entirely by setting the execution time to 0, though this isn't recommended.

set_time_limit() may be disabled by mechanisms such as disable_functions so it might not be available to you, likewise you might not have access to php.ini. If both of these are the case then you should contact your host for help.

One exception is PHP scripts run from the command line. Under these running conditions, PHP scripts may be interactive and need to spend a long time processing data or waiting for input. For this reason there isn't a max_execution_time limit on scripts run from the command line by default.


EDIT TO ADD: PHP 7's error handling had a major overhaul. I believe that errors and exceptions are now both subclasses of Throwable. This may make the above no longer relevant for PHP7+, though I'll have to look more closely into the specifics of how error handling works now to be sure.

玻璃人 2024-12-03 22:52:29

是的,我测试了 TheJanOnline 的解决方案。 sleep() 不计入 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 ); 
while(1) {/*nothing*/}
// will die after 1 sec and print error
?>

Yeah I tested the solution by TheJanOnline. sleep() does not count into php execution time so here is WORKING version with indefinite loop:

<?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
?>
浅笑依然 2024-12-03 22:52:29

你对此无能为力。但您可以使用 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
} 

?>

There is nothing you can do about it. but you can have graceful shutdown using 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
} 

?>
一向肩并 2024-12-03 22:52:29

在某些情况下,有一个有点棘手的方法来处理“致命错误:超出最大执行时间 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
}

There is a little tricky way to handle "Fatal error: Maximum execution time of 30 seconds exceeded" as exception in certain cases:

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;
}

In your code you must to measure execution time and throw exception earlier than the timeout fatal error may be triggered. $k = 0.8 is a 80% of allowed execution time, so you have 20% of time to handle exception.

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
}
笑忘罢 2024-12-03 22:52:29

我根据@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 秒并不算太长,它只需要比从我正在抓取的页面类型中提取数据的时间短一点的时间。有时,仅由于连接不正常而导致仅仅几秒钟的时间。知道有时会失败的是连接时间,而不是脚本处理,所以最好不要增加超时,而只是自动刷新页面并重试。

I came up with this based on the answer @pinkal-vansia gave. So I'm not claiming an original answer, but an answer with a practical application. I needed a way for the page to refresh itself in the event of a timeout. Since I have been observing enough timeouts of my cURL script to know the code is working, but that sometimes for whatever reason it fails to connect to the remote server, or read the served html fully, and that upon refresh the problem goes away, I am ok with script refreshing itself to "cure" a Maximum execution timeout error.

<?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.
}

FYI, 300 seconds is not too long for the scraping script I'm running, which takes just a little less than that to extract the data from the kinds of pages I'm scraping. Sometimes it goes over by just a few seconds only due to connection irregularities. Knowing that it's connection times that sometimes fail, rather than script processing, it's better to not increase the timeout, but rather just automatically refresh the page and try again.

写下不归期 2024-12-03 22:52:29

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

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

References:

I faced a similar problem and here was how I solved it:

<?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";
?>

This script, as is, is going to print Timeout! at the end.

You can modify the line $i = 0; to $i = 1 / 0; and it is going to print:

Other error: Array
(
    [type] => 2
    [message] => Division by zero
    [file] => /home/user/test.php
    [line] => 17
)

References:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文