我一直在玩我正在开发的系统并设法让它导致这个:
致命错误:超过 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);
?>
看看以下链接:
http://www.php.net/manual/en/function.set-error-handler.php#106061 http://www.php.net/manual/en/function.register-shutdown-function.php
您唯一的选择是增加脚本的允许执行时间(将其设置为 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+ 相关,尽管我必须更仔细地研究错误处理的具体细节才能确定。
您对此无能为力。但是您可以使用 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()
在无限循环之前声明,会在关机期间调用它吗?
是的,我测试了 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
?>
在某些情况下,将“致命错误:超过 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 秒并不算长,这比从我正在抓取的页面中提取数据的时间要少一点。有时,仅由于连接不规则,它只会持续几秒钟。知道有时失败的是连接时间,而不是脚本处理,最好不要增加超时,而只是自动刷新页面并重试。
我遇到了类似的问题,这就是我解决它的方法:
<?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 - 手册
PHP: set_time_limit - 手动
PHP:error_get_last - 手动