PHP Deamon

Not every problem can be solved with an aid of a cronjob. Sometimes a real time background process is required. The most common approach is to create an ordinary script and run it from command line with “&” character at the end.

I’m sure many PHP programmers asked themselves a question, is that the right way of doing it? What is the correct way? We usually don’t deal with the low level programming, but the C programmers do. I found the answer in the Advance Programming in the UNIX Environment book. There is a chapter about creating a deamon in C. It let me to create the below library which can be included into any PHP deamon script.

/**
 * _demonlib.php
 */

defined('DEAMON_LOCK_FILE') ||
    define('DEAMON_LOCK_FILE', 'run/deamon.pid');

if($_SERVER['argc'] >= 2 && $_SERVER['argv'][1] == 'kill')
{
    $fh = fopen(realpath(__DIR__) . '/' . DEAMON_LOCK_FILE, 'r');
    $pid = fread($fh, 8);

    if( $pid )
        posix_kill($pid, SIGTERM);

    exit;
}

global $DEAMON_LOCK_HANDLER;

function daemonize($signalHandler = false ) {
        global $DEAMON_LOCK_HANDLER;

        if( ! deamon_file_lock() ) {
                printf("Deamon is already running...n");
                exit();
        }

        umask(0);

        $pid = pcntl_fork();

        if( $pid < 0 ) {
                printf("Can't forkn");
                exit;
        }
        else if( $pid ) {
                exit;
        }

        $sid = posix_setsid();

        if( $sid < 0 ) {
                printf("Can't set session leadern");
                exit;
        }

        deamon_bind_signals($signalHandler);

        $pid = pcntl_fork();

        if( $pid < 0 || $pid ) {
                exit;
        }

        ftruncate($DEAMON_LOCK_HANDLER, 0);
        fwrite($DEAMON_LOCK_HANDLER, posix_getpid());

        chdir('/');

        fclose( STDIN );
        fclose( STDOUT );
        fclose( STDERR );
}

function deamon_bind_signals($signalHandler = false) {
        $signalHandler = !$signalHandler ? "deamon_signal_handler" : $signalHandler;

        pcntl_signal(SIGTERM, $signalHandler);
        pcntl_signal(SIGHUP,  $signalHandler);
        pcntl_signal(SIGUSR1, $signalHandler);
}

function deamon_file_lock() {
        global $DEAMON_LOCK_HANDLER;
        $DEAMON_LOCK_HANDLER = fopen(realpath(__DIR__) . '/' . DEAMON_LOCK_FILE, 'c');

        if( ! $DEAMON_LOCK_HANDLER ) {
                printf("Can't open lock filen");
                die();
        }
        if( !flock( $DEAMON_LOCK_HANDLER, LOCK_EX | LOCK_NB ) ) {
                return false;
        }
        return true;
}

function deamon_signal_handler($signo) {
        switch( $signo ) {
                case SIGTERM:
                case SIGHUP:
                case SIGUSR1:
                        break;
        }
}

And a small example of how to use the library.

/**
 * demon.php
 */

function sighandler($sig) {
    global $robot;

    if( $sig == SIGTERM ) {
        global $DEAMON_LOCK_HANDLER;
        fclose( $DEAMON_LOCK_HANDLER );
        $robot->message("SIGTEM exit(0)");
        exit;
    }
}

define('DEAMON_LOCK_FILE', 'run/mydeamon.pid');

require_once '_deamonlib.php';

daemonize("sighandler");

while( true ) {
    pcntl_signal_dispatch();
    // do something here
    sleep( 1 );
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s