Question

This is the complete reproducible code.

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

When I run this I see the date values each second. Now if I press Ctrl+C cleanup function is not called. In fact signal_handler is not called.

Here is the sample output.

$ 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
Was it helpful?

Solution

The CTRL+C fires SIGINT, which you have not installed handler for:

<?php
...
function signal_handler($signo){
...
    case SIGINT:
        // handle restart tasks
        cleanup();
        break;
...
}
...
pcntl_signal(SIGINT, "signal_handler");
...

Now it works:

$ 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

OTHER TIPS

It's important to understand how declare works: http://www.php.net/manual/en/control-structures.declare.php

it either needs to wrap what ever it's affecting to be declared in the 'root' script (index.php or something similar).

fun fact; the IF check around declare(ticks = 1); doesn't matter, if you remove those 3 lines the script will stop working, if you change the check to if (false) it will work, the declare seems to be evaluated outside of the normal code flow xD

Following Ruben de Vries correction to the compile-time use of declare ticks, there are two things wrong with the original code (other than missing the INT signal).

As Ruben says:

if(false) declare(ticks=1);

This DOES declare ticks to be handled. Try it! The docs go on to say how you cannot use variables etc. It's a compile time hack.

Furthermore the use of pcntl_signal_dispatch() is not a straight replacement for declaring ticks - it must be called during code execution - like ticks do automatically. So calling it once in the head of script like that will only process any signals pending at that moment.

To make pcntl_signal_dispatch() operate like using ticks to drive it you'd need to sprinkle it all over your code... depending on precisely where/when you want the interrupts to take effect.

At least:

while(true){
    sleep(1);
    echo date(DATE_ATOM). "\n";
    pcntl_signal_dispatch();
}

So the original code is actually using ticks for all versions of PHP, and additionally checking for just one signal received during the initial boot process if version is greater than 5.3. Not what the OP intended.

Personally I find the use of ticks to drive PCNTL signals a huge messy pain. The "loop" in my case is in third party code, so I cannot add a dispatch method, and it the declare ticks at the top level doesn't work on production unless I add it to every file (some autoload, scoping or PHP7 version specific issue I think).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top