2010-08-04 90 views
7

我正在運行第三方腳本,通過使用我編寫的包裝類來調用shell_exec()和管道,然後使用php代碼稍後解析。我應該提到這是行得通的,但我試圖增強功能,遇到了我沒有想到的用例。shell_exec()timeout management&exec()

如何最好的管理shell_exec()的超時?我正在考慮將其包裝在try() catch()中,但我不確定如何最好地處理時間組件。

我一直在這裏與shell_exec()exec()讀了幾個問題,似乎通過傳遞輸出PARAMS到exec()你可以得到回報的,但這依賴於腳本返回狀態完成。另外在我的小測試頁面中,我似乎無法讓它返回任何輸出!

我想過的另一個選擇是使用模式對話框,使用ajax樣式的微調器,同時使用腳本,並在JavaScript中設置手動超時。然後給用戶一個關於失敗/超時和結束的模型對話框消息。

這個用例是否有任何可接受的方法?

我的小測試,由以下部分組成,

public $e_return = array(); 
public $e_status = ''; 
// Paths are absolute from/
public function execCheck($domain){ 
    exec($this->ssl_check_path." -s ".$domain." -p 443 > ".$this->folder.$this->filename." 2>&1 &", &$this->e_return, &$this->e_status); 
} 

// Returns 
Array 
(
) 

0 

使用這個問題作爲參考, Can't execute PHP script using PHP exec

http://www.php.net/manual/en/function.exec.php

回答

7

我建議你考慮使用proc_open。您可以將其配置爲返回流資源,手動保留計時器,並且如果計時器在過程完成之前到期,則可以使用proc_terminate終止計時器。如果它在計時器到期之前完成,那麼您可以使用proc_close,然後使用stream_get_contents來獲取本來會寫入標準輸出的數據。

http://www.php.net/manual/en/function.proc-open.php

+0

感謝我給一個嘗試今天看我怎麼去:) – 2010-08-05 07:55:03

+1

我也發現了這個問題,非常方便,http://stackoverflow.com/questions/2603912/php-set-timeout-for-script-with-system-call-set-time-limit-not-working – 2010-08-05 13:18:07

15

我寫了這樣的任務的一些工作一段代碼。函數返回退出代碼(0 - OK,> 0 - 錯誤)並將stdout stderr寫入引用變量。

/*execute program and write all output to $out 
terminate program if it runs more than 30 seconds */ 
execute("program --option", null, $out, $out, 30); 
echo $out; 

function execute($cmd, $stdin=null, &$stdout, &$stderr, $timeout=false) 
{ 
    $pipes = array(); 
    $process = proc_open(
     $cmd, 
     array(array('pipe','r'),array('pipe','w'),array('pipe','w')), 
     $pipes 
    ); 
    $start = time(); 
    $stdout = ''; 
    $stderr = ''; 

    if(is_resource($process)) 
    { 
     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(is_resource($process)) 
    { 
     //echo "."; 
     $stdout .= stream_get_contents($pipes[1]); 
     $stderr .= stream_get_contents($pipes[2]); 

     if($timeout !== false && time() - $start > $timeout) 
     { 
      proc_terminate($process, 9); 
      return 1; 
     } 

     $status = proc_get_status($process); 
     if(!$status['running']) 
     { 
      fclose($pipes[1]); 
      fclose($pipes[2]); 
      proc_close($process); 
      return $status['exitcode']; 
     } 

     usleep(100000); 
    } 

    return 1; 
} 
0

我試過popen(),但沒有辦法終止以後的進程。 另外,即使在Windows上使用stream_set_blocking時,stream_get_contents()也會阻止該流,因此我必須改用fread。此外,proc_terminate在Windows上無法正常工作,所以我不得不使用替代kill函數。

我想出了這一點,應該在Windows和Linux工作現在都:

function execute($command, $timeout = 5) { 
    $handle = proc_open($command, [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipe); 

    $startTime = microtime(true); 

    /* Read the command output and kill it if the proccess surpassed the timeout */ 
    while(!feof($pipe[1])) { 
     $read .= fread($pipe[1], 8192); 
     if($startTime + $timeout < microtime(true)) break; 
    } 

    kill(proc_get_status($handle)['pid']); 
    proc_close($handle); 

    return $read; 
} 

/* The proc_terminate() function doesn't end proccess properly on Windows */ 
function kill($pid) { 
    return strstr(PHP_OS, 'WIN') ? exec("taskkill /F /T /PID $pid") : exec("kill -9 $pid"); 
}