PHP Process Execution Timeout
我有以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /** * 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 & on),则无法从\\\\" freeze \\\\"冻结它linux和Windows上的start /B $cmd )
我根本不介意\\"冻结\\\\"。我只需要实现该超时。
注意:该解决方案必须跨平台兼容,这一点很重要。同样重要的是,不必更改
我发现了一些资源可能会有所帮助:
- 从PHP脚本运行perl文件但不等待Windows Server
- PHP为系统调用的脚本设置超时,set_time_limit不起作用
- http://www.shapeshifter.se/2008/08/04/asynchronous-用PHP执行背景/
代码上有一些错误。
这实际上是有效的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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 ); } |
而不是
我一起扔了一些东西,以证明我在想的是可行的-此代码未经测试,没有任何保证,但可能会发送给您在正确的方向。 ;)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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 ); } |
这似乎对我有用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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]\ $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; } |
我不确定