⬆️ ⬇️

Class for implementing UNIX daemons in PHP

Well, let's start with the fact that quite often one has to deal with the fact that it is necessary to implement any server part to process some data, etc. Naturally, it would be most convenient to implement the server part as a daemon. At one time I came across a similar class of implementation of demons written in Python. And last week I decided to write the same creation in PHP, it seemed to work well, to evaluate you.



So, let's start with the fact that all the sources are on bitbucket.org and GitHub (to someone as convenient), the documentation is also written there. Actually, this is the code for the class itself:



<?php /* Author: Petr Bondarenko E-mail: public@shamanis.com Date: 31 May 2012 License: BSD Description: Class for create UNIX-daemon */ class DaemonException extends Exception {} abstract class DaemonPHP { protected $_baseDir; protected $_chrootDir = null; protected $_pid; protected $_log; protected $_err; /** *  .    pid- * @param string $path    PID- */ public function __construct($path=null) { $this->_baseDir = dirname(__FILE__); $this->_log = $this->_baseDir . '/daemon-php.log'; $this->_err = $this->_baseDir . '/daemon-php.err'; if ($path === null) { $this->_pid = $this->_baseDir . '/daemon-php.pid'; } else { $this->_pid = $path; } } /** *    log- * @param string $path    log- * @return DaemonPHP */ final public function setLog($path) { $this->_log = $path; return $this; } /** *    err- * @param string $path    err- * @return DaemonPHP */ final public function setErr($path) { $this->_err = $path; return $this; } /** *    , *     chroot   . *       . * @param string $path   chroot- */ final public function setChroot($path) { if (!function_exists('chroot')) { throw new DaemonException('Function chroot() has no. Please update you PHP version.'); } $this->_chrootDir = $path; return $this; } /** *    ,  double fork */ final protected function demonize() { $pid = pcntl_fork(); if ($pid == -1) { throw new DaemonException('Not fork process!'); } else if ($pid) { exit(0); } posix_setsid(); chdir('/'); $pid = pcntl_fork(); if ($pid == -1) { throw new DaemonException('Not double fork process!'); } else if ($pid) { $fpid = fopen($this->_pid, 'wb'); fwrite($fpid, $pid); fclose($fpid); exit(0); } posix_setsid(); chdir('/'); ini_set('error_log', $this->_baseDir . '/php_error.log'); fclose(STDIN); fclose(STDOUT); fclose(STDERR); $STDIN = fopen('/dev/null', 'r'); if ($this->_chrootDir !== null) { chroot($this->_chrootDir); } $STDOUT = fopen($this->_log, 'ab'); if (!is_writable($this->_log)) throw new DaemonException('LOG-file is not writable!'); $STDERR = fopen($this->_err, 'ab'); if (!is_writable($this->_err)) throw new DaemonException('ERR-file is not writable!'); $this->run(); } /** *   PID  * @return int PID  */ final protected function getPID() { if (file_exists($this->_pid)) { $pid = (int) file_get_contents($this->_pid); if (posix_kill($pid, SIG_DFL)) { return $pid; } else { //   ,  PID-  unlink($this->_pid); return 0; } } else { return 0; } } /** *       demonize() */ final public function start() { if (($pid = $this->getPID()) > 0) { echo "Process is running on PID: " . $pid . PHP_EOL; } else { echo "Starting..." . PHP_EOL; $this->demonize(); } } /** *    */ final public function stop() { if (($pid = $this->getPID()) > 0) { echo "Stopping ... "; posix_kill($pid, SIGTERM); unlink($this->_pid); echo "OK" . PHP_EOL; } else { echo "Process not running!" . PHP_EOL; } } /** *      stop()  start() */ final public function restart() { $this->stop(); $this->start(); } /** *     */ final public function status() { if (($pid = $this->getPID()) > 0) { echo "Process is running on PID: " . $pid . PHP_EOL; } else { echo "Process not running!" . PHP_EOL; } } /** *      */ final public function handle($argv) { switch ($argv[1]) { case 'start': $this->start(); break; case 'stop': $this->stop(); break; case 'restart': $this->restart(); break; case 'status': $this->status(); break; default: echo "Unknown command!" . PHP_EOL . "Use: " . $argv[0] . " start|stop|restart|status" . PHP_EOL; break; } } /** *   ,    . *    */ abstract public function run(); } ?> 




Immediately I apologize for the fact that the code is not too commented out in detail, I promise to fix it. So far, I have written only phpdoc sections. To implement your daemon, you need to inherit from the DaemonPHP class and implement an abstract run () method, in which your daemon code will be:

')

 <?php require_once 'daemon.php'; class MyDaemon extends DaemonPHP { public function run() { while (true) { } } } $daemon = new MyDaemon('/tmp/test.pid'); $daemon->setChroot('/home/shaman/work/PHPTest/daemon') //   chroot ->setLog('/my.log') ->setErr('/my.err') // chroot     /home/shaman/work/PHPTest/daemon ->handle($argv); } ?> 




Consider the above code. We have created the MyDaemon class, which inherits the abstract DaemonPHP class. All methods in the DaemonPHP class are declared as final , except for one that is an abstract run () method. The code that your daemon should execute is placed in the body of this method. In our case, this is just an empty endless loop to see the daemon working. Next, we created the $ daemon object of the MyDaemon class, the absolute path is passed to the constructor, where the PID file of the daemon will be created, if you do not pass this parameter, then by default the PID file will be created in the same directory as the daemon file -php.pid . Next we set the directory to execute chroot by setChroot () method, this was added immediately for security reasons, but this is not necessary. By the way, to perform chroot, it may be necessary to start the daemon as root. Further, the absolute paths for the LOG-file and the ERR-file are specified. If these parameters are not specified, then the daemon-php.log and daemon-php.err files will be created in the current directory. In the future, I think to expand the constructor so that all these options can be passed directly to the constructor. Next, we call the handle () method, in which we pass the command line arguments $ argv . This method is made specifically so that you do not think about creating a switch-case construct for processing command line arguments. But, nevertheless, you can not call this method, but to make something of your own, the class has public methods start () , stop () , restart () , status () . The names of the methods speak for themselves; in fact, these same arguments expect to get handle () .



I draw your attention to the fact that in the current directory the file php_error.log may appear, it appears only when errors occur in PHP itself and writes a log of these errors to it.

Save the file with the code, for example, under the name run.php and run:



user@localhost:~$ php run.php start

Starting...

user@localhost:~$




The status can be checked with the appropriate command:



user@localhost:~$ php run.php status

Process is running on PID: 6539




Well, respectively, stop the demon:



user@localhost:~$ php run.php stop

Stopping ... OK




The most recent code of this class is always available on the repository (the link was higher). Well, that's all, I look forward to your comments and tips on how to refine and supplement the functionality.

Source: https://habr.com/ru/post/145136/



All Articles