我一直在玩我的开发系统,成功导致了这个问题:
致命错误:超出30秒最大执行时间
当我做一些不现实的事情时发生了这个问题,但是它确实可能会发生在用户身上。
有人知道是否有一种方法可以捕获这个异常吗?我已经阅读了相关内容,但每个人似乎都建议增加允许的时间。
我一直在玩我的开发系统,成功导致了这个问题:
致命错误:超出30秒最大执行时间
当我做一些不现实的事情时发生了这个问题,但是它确实可能会发生在用户身上。
有人知道是否有一种方法可以捕获这个异常吗?我已经阅读了相关内容,但每个人似乎都建议增加允许的时间。
试试看 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);
?>
可以参考以下链接:
你唯一的选择是增加脚本允许执行的时间(将其设置为0将使它无限制,但这不是推荐的方法),或者创建一个新的线程并希望一切顺利。
之所以不能捕获这个错误是因为它实际上没有被抛出。代码中没有一行触发了这个错误,而是PHP说,“不行,很抱歉,这太长了。是时候关闭了。” 这是有道理的。想象一下,如果一个最大执行时间为30秒的脚本捕获了这个错误并花费了另外30秒的时间...在设计不良的程序中,这将开启一些相当恶意的攻击机会。至少,这将为DOS攻击创造机会。
这不是一个异常,而是一个错误。 异常和错误之间有重要的区别,首先最重要的是错误无法使用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+ 中不再相关,但我需要更仔细地了解错误处理的具体情况才能确定。
是的,我测试了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
?>
你无法改变这一点。但是,你可以使用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基于@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秒并不算太长,它只需要略微少于这个时间从我正在爬取的页面中提取数据。有时候由于连接不稳定,它可能只会超出几秒钟。知道有时候失败是因为连接时间问题而非脚本处理问题,所以最好不要增加超时时间,而是自动刷新页面并重试。
在某些情况下,处理“致命错误:超出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
}
我曾经遇到过类似的问题,这是我解决它的方法:
<?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";
?>
$i = 0;
为$i = 1 / 0;
,这时它会打印出:Other error: Array
(
[type] => 2
[message] => Division by zero
[file] => /home/user/test.php
[line] => 17
)