Обратные вызовы сигналов PHP pcntl не вызываются
Это полный воспроизводимый код.
<?php
class console{
public static function log($msg, $arr=array()){
$str = vsprintf($msg, $arr);
fprintf(STDERR, "$str\n");
}
}
function cleanup(){
echo "cleaning up\n";
}
function signal_handler($signo){
console::log("Caught a signal %d", array($signo));
switch ($signo) {
case SIGTERM:
// handle shutdown tasks
cleanup();
break;
case SIGHUP:
// handle restart tasks
cleanup();
break;
default:
fprintf(STDERR, "Unknown signal ". $signo);
}
}
if(version_compare(PHP_VERSION, "5.3.0", '<')){
// tick use required as of PHP 4.3.0
declare(ticks = 1);
}
pcntl_signal(SIGTERM, "signal_handler");
pcntl_signal(SIGHUP, "signal_handler");
if(version_compare(PHP_VERSION, "5.3.0", '>=')){
pcntl_signal_dispatch();
console::log("Signal dispatched");
}
echo "Running an infinite loop\n";
while(true){
sleep(1);
echo date(DATE_ATOM). "\n";
}
Когда я запускаю это, я вижу значения даты каждую секунду. Теперь, если я нажму Ctrl+Ccleanup
, функция не вызывается. На самом деле signal_handler
не называется.
Вот пример вывода.
$ php testsignal.php
Signal dispatched
Running an infinite loop
2012-10-04T13:54:22+06:00
2012-10-04T13:54:23+06:00
2012-10-04T13:54:24+06:00
2012-10-04T13:54:25+06:00
2012-10-04T13:54:26+06:00
2012-10-04T13:54:27+06:00
^C
3 answers
CTRL+C
запускает SIGINT
, для которого вы не установили обработчик:
<?php
...
function signal_handler($signo){
...
case SIGINT:
// handle restart tasks
cleanup();
break;
...
}
...
pcntl_signal(SIGINT, "signal_handler");
...
Теперь это работает:
$ php testsignal.php
Signal dispatched
Running an infinite loop
2012-10-08T09:57:51+02:00
2012-10-08T09:57:52+02:00
^CCaught a signal 2
cleaning up
2012-10-08T09:57:52+02:00
2012-10-08T09:57:53+02:00
^\Quit
Важно понимать, как работает declare
: http://www.php.net/manual/en/control-structures.declare.php
Ему либо нужно обернуть то, на что он влияет, чтобы быть объявленным в "корневом" скрипте(index.php или что-то подобное).
Забавный факт; проверка IF вокруг declare(ticks = 1);
не имеет значения, если вы удалите эти 3 строки, скрипт перестанет работать, если вы измените проверку на if (false)
, он будет работать, объявление, похоже, оценивается вне обычного потока кода xD
После исправления Рубеном де Врисом использования тиков объявления во время компиляции, в исходном коде есть две ошибки (кроме отсутствия сигнала INT).
Как говорит Рубен:
if(false) declare(ticks=1);
Это ДЕЙСТВИТЕЛЬНО объявляет, что тики должны быть обработаны. Попробуй! В документах далее говорится, как вы не можете использовать переменные и т. Д. Это взлом во время компиляции.
Кроме того, использование pcntl_signal_dispatch()
не является прямой заменой для объявления тиков - оно должно вызываться во время выполнения кода - как это делают клещи автоматически. Поэтому вызов его один раз в начале такого сценария будет обрабатывать только любые сигналы, ожидающие в этот момент.
Чтобы заставить pcntl_signal_dispatch() работать так, как если бы для его управления использовались галочки, вам нужно было бы разбрызгать его по всему вашему коду... в зависимости от того, где именно/когда вы хотите, чтобы прерывания вступили в силу.
По крайней мере:
while(true){
sleep(1);
echo date(DATE_ATOM). "\n";
pcntl_signal_dispatch();
}
Таким образом, исходный код фактически использует галочки для всех версий PHP, и дополнительно проверяет только для одного сигнала, полученного во время начального процесса загрузки, если версия больше 5.3. Не то, что предполагал ОП.
Лично я нахожу использование клещей для управления сигналами PCNTL огромной грязной болью. "Цикл" в моем случае находится в коде третьей стороны, поэтому я не могу добавить метод отправки, и его галочки объявления на верхнем уровне не работают на производстве, если я не добавлю его в каждый файл (я думаю, что это проблема с загрузкой, областью действия или версией PHP7).