2010-08-04 15 views
7

Estoy ejecutando un script de terceros mediante el uso de una clase contenedora que he escrito que llama a shell_exec() y canaliza a un archivo que analizo luego usando el código php. Debo mencionar que esto está funcionando, pero estoy tratando de mejorar la funcionalidad, habiendo encontrado un caso de uso en el que no había pensado.shell_exec() timeout management & exec()

¿Cómo es mejor administrar el tiempo de espera en shell_exec()? Estaba pensando en envolverlo en un try() catch() pero no estoy seguro de cómo manejar mejor el componente de tiempo.

He estado leyendo algunas preguntas aquí en relación con shell_exec() y exec() y parece que al pasar parametros de salida a exec() puede obtener un retorno, pero eso no se basan en la secuencia de comandos para terminar con un estado de retorno. Además, en mi página de mini prueba, parece que no puedo obtener ningún resultado.

La otra opción que pensé fue utilizar un diálogo modal, con un spinner de estilo ajax mientras funcionaba el script, y establecer un tiempo de espera manual en javascript. Lo cual le dio al usuario un mensaje de diálogo modelo sobre fallas/tiempo de espera y finalización.

¿Hay algún método aceptado para este caso de uso?

mi mini prueba, consistió en lo siguiente,

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 

Con esta pregunta ref, Can't execute PHP script using PHP exec

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

Respuesta

7

yo sugeriría que mirar en el uso proc_open. Puede configurarlo para devolver un recurso de transmisión, mantener manualmente un temporizador y, si el temporizador expira antes de que el proceso finalice, puede finalizarlo con proc_terminate. Si se completa antes de que expire el temporizador, puede usar proc_close y luego stream_get_contents para tomar los datos que de lo contrario se escribirían en stdout.

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

+0

Gracias Voy a dar que un intento hoy y ver cómo voy :) –

+1

También encontré esta pregunta muy útil, http://stackoverflow.com/questions/2603912/php-set-timeout-for-script-with-system-call-set-time-limit-not-working –

15

escribo alguna pieza de trabajo de código para tal tarea. La función devuelve el código de salida (0 - OK,> 0 - error) y escribe stdout, stderr en las variables de referencia.

/*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

he intentado con popen(), pero no hay manera de poner fin a la proccess después. Además, stream_get_contents() bloquea la transmisión incluso cuando se usa stream_set_blocking en Windows, así que tuve que usar fread en su lugar. Además, proc_terminate no funciona correctamente en Windows, así que tuve que usar una función de eliminación alternativa.

he ocurrió esto, que debería funcionar tanto en Windows y Linux ahora:

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"); 
}