Есть ли способ, которым вы можете прервать блок кода, если он слишком длится в PHP? Возможно, что-то вроде:
//Set the max time to 2 seconds $time = new TimeOut(2); $time->startTime(); sleep(3) $time->endTime(); if ($time->timeExpired()){ echo 'This function took too long to execute and was aborted.'; }
Это не должно быть точно так, как указано выше, но существуют ли какие-либо собственные PHP-функции или классы, которые делают что-то вроде этого?
Редактировать : ответ Бен Ли с pcnt_fork
будет идеальным решением, за исключением того, что он недоступен для Windows. Есть ли другой способ сделать это с помощью PHP, который работает для Windows и Linux, но не требует внешней библиотеки?
Редактирование 2 : Решение XzKto работает в некоторых случаях, но не последовательно, и я, похоже, не поймаю исключения, независимо от того, что я пытаюсь сделать. Вариант использования – это определение тайм-аута для единичного теста. Если тест истечет, я хочу его закончить, а затем перейти к следующему тесту.
Вы можете сделать это, разворачивая процесс, а затем используя родительский процесс для отслеживания дочернего процесса. pcntl_fork – это метод, который обрабатывает процесс, поэтому у вас есть две почти идентичные программы в памяти, работающие параллельно. Единственное различие заключается в том, что в одном процессе родительский pcntl_fork
возвращает положительное целое число, которое соответствует идентификатору процесса дочернего процесса. И в другом процессе, child, pcntl_fork
возвращает 0.
Вот пример:
$pid = pcntl_fork(); if ($pid == 0) { // this is the child process } else { // this is the parent process, and we know the child process id is in $pid }
Это основная структура. Следующий шаг – добавить истечение процесса. Ваш материал будет запущен в дочернем процессе, и родительский процесс будет отвечать только за мониторинг и синхронизацию дочернего процесса. Но для того, чтобы один процесс (родительский) убил другого (ребенка), должен быть сигнал. Сигналы – это то, как взаимодействуют процессы, а сигнал, который означает «вы должны немедленно прекратить», – это SIGKILL. Вы можете отправить этот сигнал, используя posix_kill . Таким образом, родитель должен просто подождать 2 секунды, а затем убить ребенка, например:
$pid = pcntl_fork(); if ($pid == 0) { // this is the child process // run your potentially time-consuming method } else { // this is the parent process, and we know the child process id is in $pid sleep(2); // wait 2 seconds posix_kill($pid, SIGKILL); // then kill the child }
Вы не можете этого сделать, если сценарий приостанавливается на одну команду (например, sleep ()), кроме того, что используется forking, но для особых случаев существует множество задач: например, асинхронные запросы, если вы выполняете паузу в запросе DB, proc_open, если вы программные паузы при некотором внешнем исполнении и т. д. К сожалению, они все разные, поэтому нет общего решения.
Если скрипт ждет длинный цикл / много строк кода, вы можете сделать такой трюк:
declare(ticks=1); class Timouter { private static $start_time = false, $timeout; public static function start($timeout) { self::$start_time = microtime(true); self::$timeout = (float) $timeout; register_tick_function(array('Timouter', 'tick')); } public static function end() { unregister_tick_function(array('Timouter', 'tick')); } public static function tick() { if ((microtime(true) - self::$start_time) > self::$timeout) throw new Exception; } } //Main code try { //Start timeout Timouter::start(3); //Some long code to execute that you want to set timeout for. while (1); } catch (Exception $e) { Timouter::end(); echo "Timeouted!"; }
но я не думаю, что это очень хорошо. Если вы укажете точный случай, я думаю, мы можем помочь вам лучше.
Вы можете использовать функцию объявления, если время выполнения превышает пределы. http://www.php.net/manual/en/control-structures.declare.php
Вот пример кода, как использовать
define("MAX_EXECUTION_TIME", 2); # seconds $timeline = time() + MAX_EXECUTION_TIME; function check_timeout() { if( time() < $GLOBALS['timeline'] ) return; # timeout reached: print "Timeout!".PHP_EOL; exit; } register_tick_function("check_timeout"); $data = ""; declare( ticks=1 ){ # here the process that might require long execution time sleep(5); // Comment this line to see this data text $data = "Long process result".PHP_EOL; } # Ok, process completed, output the result: print $data;
С помощью этого кода вы увидите сообщение о тайм-ауте. Если вы хотите получить результат Long process внутри блока declare, вы можете просто удалить строку sleep (5) или увеличить максимальное время выполнения, объявленное в начале скрипта
Что относительно установленного предела времени, если вы не находитесь в безопасном режиме.
Приготовил это примерно через две минуты, я забыл называть $time->startTime();
поэтому я точно не знаю, сколько времени прошло;)
class TimeOut{ public function __construct($time=0) { $this->limit = $time; } public function startTime() { $this->old = microtime(true); } public function checkTime() { $this->new = microtime(true); } public function timeExpired() { $this->checkTime(); return ($this->new - $this->old > $this->limit); } }
И демо .
Я действительно не понимаю, что делает ваш endTime()
, поэтому вместо этого я сделал checkTime()
, который также не имеет реальной цели, а обновляет внутренние значения. timeExpired()
вызывает это автоматически, потому что он наверняка воняет, если вы забыли вызвать checkTime()
и он использовал старые времена.
Это старый вопрос и, вероятно, уже много раз решался, но для людей, которые ищут простой способ решить эту проблему, теперь есть библиотека: PHP Invoker .
Вы также можете использовать второй скрипт, который имеет в нем код паузы, который выполняется с помощью зависающего вызова с установленным таймаутом. Другим очевидным решением является устранение причины паузы.