PHP为系统调用的脚本设置超时,set_time_limit不起作用

时间:2010-04-08 22:33:25

标签: php windows linux command-line

我有一个命令行PHP脚本,它使用foreach的数组的每个成员运行wget请求。这个wget请求有时可能需要很长时间,因此我希望能够设置超时以杀死脚本(例如,如果超过15秒)。我已禁用PHP安全模式并在脚本的早期尝试了set_time_limit(15),但它会无限期地继续。 更新:感谢Dor指出这是因为set_time_limit()不尊重system()调用。

所以我试图在执行15秒后找到其他方法来杀死脚本。但是,我不确定是否可以检查脚本在同时处于wget请求中间时运行的时间(while循环不起作用)。也许用一个定时器分叉一个进程并将其设置为在一段时间后杀死父进程?

感谢您的任何提示!

更新 以下是我的相关代码。 $ url是从命令行传递的,是一个包含多个URL的数组(对不起,最初不发布):

foreach( $url as $key => $value){
    $wget = "wget -r -H -nd -l 999 $value";
    system($wget);
    }

4 个答案:

答案 0 :(得分:6)

除了--timeout之外,请尝试使用wget命令行参数set_time_limit()

请记住set_time_limit(15) 从零开始重启超时计数器所以不要在循环中调用它(为了您的目的)

来自man wget

  

- 超时=秒

     

设置网络   超时到秒秒。这是   相当于指定   --dns-timeout, - connect-timeout和--read-timeout,所有这些都在同一时间。

     

与网络互动时,   Wget可以检查超时和中止   操作如果花费太长时间。   这可以防止像吊挂这样的异常   读取和无限连接。唯一的   默认情况下启用超时为a   900秒读取超时。设置一个   超时为0会完全禁用它。   除非你知道你在做什么,否则它   最好不要更改默认值   超时设置。

     

所有与超时相关的选项均可接受   十进制值,以及亚秒   值。例如,0.1秒是a   合法(虽然不明智)的选择   超时。亚秒超时   用于检查服务器响应   时间或测试网络延迟。

编辑:好的。我明白你现在在做什么。您应该做的是使用proc_open代替system,并使用time()函数检查时间,如果wget tskes太长则调用proc_terminate

答案 1 :(得分:4)

您可以将wget程序的输出定向到一个文件,然后立即返回 另见:

  

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

资料来源:set_time_limit() @ php.net

答案 2 :(得分:4)

您可以使用“--timeout”和time()的组合。首先要弄清楚总共有多少时间,然后在脚本运行时降低--timeout。

例如:

$endtime = time()+15;
foreach( $url as $key => $value){
  $timeleft = $endtime - time();
  if($timeleft > 0) {
    $wget = "wget -t 1 --timeout $timeleft $otherwgetflags $value";
    print "running $wget<br>";
    system($wget);
  } else {
    print("timed out!");
    exit(0);
  }
}

注意:如果你不使用-t,wget将尝试20次,每次等待 - 超时秒。

这是一些使用proc_open / proc_terminate的示例代码(@Josh的建议):

$descriptorspec = array(
   0 => array("pipe", "r"),
   1 => array("pipe", "w"),
   2 => array("pipe", "w")
);
$pipes = array();

$endtime = time()+15;
foreach( $url as $key => $value){
  $wget = "wget $otherwgetflags $value";
  print "running $wget\n";
  $process = proc_open($wget, $descriptorspec, $pipes);
  if (is_resource($process)) {
    do {
      $timeleft = $endtime - time();
      $read = array($pipes[1]);
      stream_select($read, $write = NULL, $exeptions = NULL, $timeleft, NULL);
      if(!empty($read)) {
        $stdout = fread($pipes[1], 8192);
        print("wget said--$stdout--\n");
      }
    } while(!feof($pipes[1]) && $timeleft > 0);
    if($timeleft <= 0) {
      print("timed out\n");
      proc_terminate($process);
      exit(0);
    }
  } else {
    print("proc_open failed\n");
  }
}

答案 3 :(得分:0)

我认为最好的方法是处理命令本身的查杀,至少在Linux上是这样(Ubuntu 17.10)。没有其他方法对我有用...... proc_terminate来自另一个答案,但没有输出。

在下面的示例中,我添加了一个魔术字符串,用于检查您的进程是否仍在运行。如果运行时间超过$sec,则会立即终止该过程,并留下“Timeout!”的注释。

像这样,我确信命令不会超过超时,我会收到我需要的所有stdout / stderr。

$wget = "wget -r -H -nd -l 999 $value"; // your command

$sec = 5; // timeout in seconds
$cmd = $wget . " & ii=0 && while [ \"2\" -eq \"`ps -p $! | wc -l \" ]; do ii=$((ii+1)); if [ \$ii -gt ".($sec)."0 ]; then echo 'Timeout!';kill $!; break;fi; sleep 0.1; done"
system($cmd); // system call