2011-03-15 87 views
2

我有以下代码:PHP流程执行超时

/** 
* Executes a program and waits for it to finish, taking pipes into account. 
* @param string $cmd Command line to execute, including any arguments. 
* @param string $input Data for standard input. 
* @param integer $timeout How much to wait from program in msecs (-1 to wait indefinitely). 
* @return array Array of "stdout", "stderr" and "return". 
*/ 
function execute($cmd,$stdin=null,$timeout=-1){ 
    $proc=proc_open(
     $cmd, 
     array(array('pipe','r'),array('pipe','w'),array('pipe','w')), 
     $pipes=null 
    ); 
    fwrite($pipes[0],$stdin);     fclose($pipes[0]); 
    $stdout=stream_get_contents($pipes[1]); fclose($pipes[1]); 
    $stderr=stream_get_contents($pipes[2]); fclose($pipes[2]); 
    $return=proc_close($proc); 
    return array(
     'stdout' => $stdout, 
     'stderr' => $stderr, 
     'return' => $return 
    ); 
} 

它有两个 “问题”。

  • 该代码是同步的;它会冻结,直到目标进程关闭。
  • 到目前为止,我还没有,而不会发出不同类型的命令能够从“冻结”(如$cmd > /dev/null &在Linux和start /B $cmd在Windows上)

我不介意“冻结“,完全可以。我只需要实现那个超时。

注意:该解决方案跨平台兼容非常重要。 $cmd不必更改也很重要 - 我运行的是一些复杂的命令,但恐怕可能会出现一些问题,但这取决于修复的类型 - 我很高兴听到这些信息,只是我更喜欢不同的选择。

我发现一些资源,可以帮助:

回答

1

而不是stream_get_contents,你可以看看使用fread获得更多的细粒度控制你的代码在做什么。结合stream_set_timeout可能会给你你想要的。

我把一些东西放在一起作为我想要的东西的演示 - 这些代码完全没有经过测试,没有任何保证,但可能会向正确的方向发送。 ;)

function execute($cmd,$stdin=null,$timeout=-1){ 
    $proc=proc_open(
     $cmd, 
     array(array('pipe','r'),array('pipe','w'),array('pipe','w')), 
     $pipes=null 
    ); 
    fwrite($pipes[0],$stdin);     fclose($pipes[0]); 

    stream_set_timeout($pipes[1], 0); 
    stream_set_timeout($pipes[2], 0); 

    $stdout = ''; 

    $start = microtime(); 

    while ($data = fread($pipes[1], 4096)) 
    { 
     $meta = stream_get_meta_data($pipes[1]); 
     if (microtime()-$start>$timeout) break; 
     if ($meta['timed_out']) continue; 
     $stdout .= $data; 
    } 

    $return = proc_close($proc); 
    $stdout .= stream_get_contents($pipes[1]); 
    $stderr = stream_get_contents($pipes[2]); 

    return array(
     'stdout' => $stdout, 
     'stderr' => $stderr, 
     'return' => $return 
    ); 
} 
2

代码有一些错误。

,实际上是工作:

function execute($cmd, $stdin = null, $timeout = -1) 
{ 
    $proc=proc_open(
     $cmd, 
     array(array('pipe','r'), array('pipe','w'), array('pipe','w')), 
     $pipes 
    ); 
    var_dump($pipes); 
    if (isset($stdin)) 
    { 
     fwrite($pipes[0],$stdin); 
    } 
    fclose($pipes[0]); 

    stream_set_timeout($pipes[1], 0); 
    stream_set_timeout($pipes[2], 0); 

    $stdout = ''; 

    $start = microtime(); 

    while ($data = fread($pipes[1], 4096)) 
    { 
     $meta = stream_get_meta_data($pipes[1]); 
     if (microtime()-$start>$timeout) break; 
     if ($meta['timed_out']) continue; 
     $stdout .= $data; 
    } 

    $stdout .= stream_get_contents($pipes[1]); 
    $stderr = stream_get_contents($pipes[2]); 
    $return = proc_close($proc); 

    return array(
     'stdout' => $stdout, 
     'stderr' => $stderr, 
     'return' => $return 
    ); 
} 
0

这似乎是为我工作:

public function toPDF() { 
    $doc = $this->getDocument(); 

    $descriptor = [ 
     ['pipe','r'], 
     ['pipe','w'], 
     ['file','/dev/null','w'], // STDERR 
    ]; 
    $proc = proc_open('/usr/local/project/scripts/dompdf_cli.php',$descriptor,$pipes,sys_get_temp_dir()); 
    fwrite($pipes[0],"$doc[paper]\n$doc[html]"); 
    fclose($pipes[0]); 

    $timeout = 30; 

    stream_set_blocking($pipes[1], false); 

    $pdf = ''; 

    $now = microtime(true); 

    try { 
     do { 
      $elapsed = microtime(true) - $now; 

      if($elapsed > $timeout) { 
       throw new \Exception("PDF generation timed out after $timeout seconds"); 
      } 
      $data = fread($pipes[1], 4096); 
      if($data === false) { 
       throw new \Exception("Read failed"); 
      } 
      if(strlen($data) === 0) { 
       usleep(50); 
       continue; 
      } 
      $pdf .= $data; 
     } while(!feof($pipes[1])); 

     fclose($pipes[1]); 
     $ret = proc_close($proc); 
    } catch(\Exception $ex) { 
     fclose($pipes[1]); 
     proc_terminate($proc); // proc_close tends to hang if the process is timing out 
     throw $ex; 
    } 


    if($ret !== 0) { 
     throw new \Exception("dompdf_cli returned non-zero exit status: $ret"); 
    } 

    // dump('returning pdf'); 
    return $pdf; 
} 

我不知道什么样的stream_set_timeout目的是 - 这只是设置per-读取超时,但如果您想限制整体时间,则只需将流设置为非阻塞模式,然后将时间设置为需要多长时间。