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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
/** * _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 fork\n"); exit; } else if( $pid ) { exit; } $sid = posix_setsid(); if( $sid < 0 ) { printf("Can't set session leader\n"); 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 file\n"); 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/** * 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 ); } |