Я запускаю сторонний скрипт, используя класс оболочки, который я написал, который вызывает shell_exec()
и pipe в файл, который я анализирую позже, используя php-код. Я должен упомянуть, что это работает, но я пытаюсь улучшить функциональность, столкнувшись с прецедентом, о котором я не думал.
Как лучше всего управлять таймаутом на shell_exec ()? Я думал об обертке в try() catch()
но я не уверен, как лучше всего обрабатывать компонент времени.
Я читал здесь несколько вопросов относительно shell_exec()
и exec()
и кажется, что, передавая выходные параметры 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
Используя этот вопрос как ref, не удается выполнить PHP-скрипт с помощью PHP exec
http://www.php.net/manual/en/function.exec.php
Я предлагаю вам изучить использование proc_open
. Вы можете настроить его для возврата ресурса потока, вручную сохранить таймер, и если таймер истечет до завершения процесса, вы можете завершить его с помощью proc_terminate
. Если он завершится до истечения срока действия таймера, вы можете использовать proc_close
затем stream_get_contents
чтобы захватить данные, которые иначе были бы записаны в stdout.
Я пишу часть рабочего кода для такой задачи. Функция возвращает код выхода (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; }
в/*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; }
Я пробовал с помощью popen()
, но после этого невозможно завершить процесс. Кроме того, stream_get_contents()
блокирует поток даже при использовании stream_set_blocking в Windows, поэтому мне пришлось использовать 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"); }