PHP:set_time_limit()似乎没有任何效果

时间:2011-03-30 08:52:41

标签: php timeout

我正在尝试在普通的PHP apache脚本(而不是CLI)中使用PHP的set_time_limit()来限制其最大运行时间。它没有对系统调用或类似的东西做任何事情,我们没有使用安全模式(我们是所有者,而不是服务器的管理员)。这是Linux(Ubuntu)上的PHP 5.2.4。

简单的测试用例:

ini_set('max_execution_time', 1);
set_time_limit(1);
$i=0;
while ($i++<100000000000) {} // or anything other arbitrary that takes time
die('Done');

预期结果:与超出脚本执行时间相关的内容

实际结果:已完成。

我做错了吗? (顺便说一下,是的,脚本确实需要并且可以运行太长时间并且需要中止,遗憾的是这不是可以避免的。这不是重点。)

6 个答案:

答案 0 :(得分:3)

从手册中引用:

  

注意:

     

set_time_limit()函数和   配置指令   max_execution_time只影响   脚本本身的执行时间。   花在活动上的任何时间   发生在执行之外   脚本如系统调用使用   system(),流操作,数据库   查询等不包括在内   确定最大时间   脚本一直在运行。这不是   在测量的Windows上是真的   时间是真实的。

sleep()是那些不影响脚本运行时间的函数之一

请参阅MPHH对手册

的sleep()页面的评论
  

注意:set_time_limit()函数   和配置指令   max_execution_time只影响   脚本本身的执行时间。   花在活动上的任何时间   发生在执行之外   脚本如系统调用使用   system(),sleep()函数,   不包括数据库查询等   在确定最大时间时   该脚本一直在运行。

答案 1 :(得分:1)

在某些系统上,出于安全性和资源共享的原因,禁用了ini_set和类似的功能。请咨询您的管理员,了解是否存在此类限制。

答案 2 :(得分:1)

这是因为时间限制忽略了函数sleep,它会在睡眠激活时停止计时,并在停用时再次开始计数。

答案 3 :(得分:1)

尝试让你的脚本真正做一些事情,睡眠时间不计入执行时间,因为当脚本处于休眠状态时它不会执行。

编辑:查看此错误报告http://bugs.php.net/37306最后一条评论是修复了“CVS HEAD,PHP_5_2和PHP_5_1”。所以也许你的PHP版本有这个bug。也许您可以尝试更改max_input_time

答案 4 :(得分:1)

max_execution_time(...)和ini_set(&#39; max_execution_time&#39;,...)都不会计算sleep(),file_get_contents(),shell_exec(),mysql_query的时间成本( )等等,所以我构建了以下函数my_background_exec()来在后台/分离进程上运行静态方法,当时间结束时,自动终止它:

my_exec.php:

<?php
function my_background_exec($function_name, $params, $str_requires, $timeout=600)
         {$map=array('"'=>'\"', '$'=>'\$', '`'=>'\`', '\\'=>'\\\\', '!'=>'\!');
          $str_requires=strtr($str_requires, $map);
          $path_run=dirname($_SERVER['SCRIPT_FILENAME']);
          $my_target_exec="/usr/bin/php -r \"chdir('{$path_run}');{$str_requires}\\\$params=json_decode(file_get_contents('php://stdin'),true);call_user_func_array('{$function_name}', \\\$params);\"";
          $my_target_exec=strtr(strtr($my_target_exec, $map), $map);
          $my_background_exec="(/usr/bin/php -r \"chdir('{$path_run}');{$str_requires}my_timeout_exec(\\\"{$my_target_exec}\\\", file_get_contents('php://stdin'), {$timeout});\" <&3 &) 3<&0";//php by default use "sh", and "sh" don't support "<&0"
          my_timeout_exec($my_background_exec, json_encode($params), 2);
         }

function my_timeout_exec($cmd, $stdin='', $timeout)
         {$start=time();
          $stdout='';
          $stderr='';
          //file_put_contents('debug.txt', time().':cmd:'.$cmd."\n", FILE_APPEND);
          //file_put_contents('debug.txt', time().':stdin:'.$stdin."\n", FILE_APPEND);

          $process=proc_open($cmd, [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipes);
          if (!is_resource($process))
             {return array('return'=>'1', 'stdout'=>$stdout, 'stderr'=>$stderr);
             }
          $status=proc_get_status($process);
          posix_setpgid($status['pid'], $status['pid']);    //seperate pgid(process group id) from parent's pgid

          stream_set_blocking($pipes[0], 0);
          stream_set_blocking($pipes[1], 0);
          stream_set_blocking($pipes[2], 0);
          fwrite($pipes[0], $stdin);
          fclose($pipes[0]);

          while (1)
                {$stdout.=stream_get_contents($pipes[1]);
                 $stderr.=stream_get_contents($pipes[2]);

                 if (time()-$start>$timeout)
                    {//proc_terminate($process, 9);    //only terminate subprocess, won't terminate sub-subprocess
                     posix_kill(-$status['pid'], 9);    //sends SIGKILL to all processes inside group(negative means GPID, all subprocesses share the top process group, except nested my_timeout_exec)
                     //file_put_contents('debug.txt', time().":kill group {$status['pid']}\n", FILE_APPEND);
                     return array('return'=>'1', 'stdout'=>$stdout, 'stderr'=>$stderr);
                    }

                 $status=proc_get_status($process);
                 //file_put_contents('debug.txt', time().':status:'.var_export($status, true)."\n";
                 if (!$status['running'])
                    {fclose($pipes[1]);
                     fclose($pipes[2]);
                     proc_close($process);
                     return $status['exitcode'];
                    }

                 usleep(100000); 
                }
         }
?>

test.php的:

<?php
my_background_exec('A::jack', array('hello ', 'jack'), 'require "my_exec.php";require "a_class.php";', 8);
?>

a_class.php:

<?php
class A
{
    static function jack($a, $b)
           {sleep(4);
            file_put_contents('debug.txt', time().":A::jack:".$a.' '.$b."\n", FILE_APPEND);
            sleep(15);
           }
}
?>

答案 5 :(得分:0)

通过检查php.ini中的disable_functions标志来检查你的ini_set是否有效。如果没有禁用,请在你的ini_set('max_execution_time',1)之后立即回复phpinfo();以上。如果在本地列下更改了值,那么您知道ini_set有效。否则,您的脚本没有设置任何最长执行时间。