rogerthomas84 / daemoniser
Daemoniser is a PHP library to help make daemon applications
Requires
- php: >=5.0
- ext-pcntl: *
- ext-posix: *
README
__ \ _)
| | _` | _ \ __ `__ \ _ \ __ \ | __| _ \ __|
| | ( | __/ | | | ( | | | |\__ \ __/ |
____/ \__,_|\___|_| _| _|\___/ _| _|_|____/\___|_|
What is it
Daemoniser is a PHP library to take the stress off of creating PHP based daemons. If you're questioning why then this library isn't for you. Generally PHP shouldn't be used for creating daemons.
Use it with Composer
$ composer require rogerthomas84/daemoniser
How to use
For help writing your first daemon, see the example in example/example.php
.
A daemon requires an instance of DaemonConfig
to be passed in via the execute
method.
DaemonConfig constructor:
DaemonAbstract $damon
/ The instance of your daemon (which extendsDaemonAbstract
)- Required
$errorLogFilePath
/ A full path to either a new or existing log file to use for logging errors.- Optional, defaults to
tmp/log/error_My_Class_Name.log
(assuming fully qualified class name is\My\Class\Name
)
- Optional, defaults to
$infoLogFilePath
/ A full path to either a new or existing log file to use for logging echo'd content.- Optional, defaults to
tmp/log/info_My_Class_Name.log
(assuming fully qualified class name is\My\Class\Name
)
- Optional, defaults to
$pidFilePath
/ A full path to either a new or existing file to use for storing the pid of the dameon'd instance.- Optional, defaults to
tmp/pid/My_Class_Name.pid
(assuming fully qualified class name is\My\Class\Name
)
- Optional, defaults to
$softStopFilePath
/ The path to the soft stop file.- Optional, defaults to
tmp/stops/stop_My_Class_Name.log
(assuming fully qualified class name is\My\Class\Name
)
- Optional, defaults to
$sleepDuration
/ How long to sleep after each call to yourrun()
method.- Optional, defaults to
5
seconds
- Optional, defaults to
$maxLogFileSize
/ How large in bytes should your log file be allowed to get before- Optional, defaults to
100000000
bytes, (100 MB)
- Optional, defaults to
$iniSettings
/ A key => value array of settings to use withini_set
. For example,['memory_limit' => '1024MB']
- Optional, defaults to empty array
$whitelistedUsers
/ An array containing the usernames allowed to execute this command.- Optional, defaults to empty array, allowing any user.
All config options are also available set
and get
methods in the config object.
Usage
Daemoniser has several helpful commands.
php my-daemon.php status
- Get the status of the daemonphp my-daemon.php start
- Start the daemonphp my-daemon.php soft-stop
- Stop the daemon via a stop file gracefully.php my-daemon.php stop
- Stop the daemon immediately (this is not ideal)php my-daemon.php restart
- Restart the daemon (this is not ideal)php my-daemon.php pid
- Get the PID for the daemonphp my-daemon.php rm-logs
- Delete historic logs for this daemon.php my-daemon.php rm-logs all
- Delete ALL logs for this daemon.php my-daemon.php help
- Show the help content
You shouldn't ever use stop
really. Doing so will result in the process being immediately killed. But this is less
of a problem if you aren't running something that could result in data loss in the case of an immediate halt being
called.
If you 'must' have an immediate stop, you'll have to implement the canImmediatelyStop()
method in your daemons
and return true
. This should be avoided at all costs, as the stop process simply kills the pid
immediately.
Extending the commands:
You can easily extend the commands to perform different actions outside of the run()
loop. To do this, simply
implement the protected function getAdditionalCommands()
method.
You'd need to return an array of objects. Each object being an instance of DaemonCommand
. There's a helpful
::build()
method available on the DaemonCommand
which you can use to easily build new functionality.
Should I even use this?
If you're asking yourself this question, then the answer is no. No you shouldn't. This library fills very specific requirements.
Full example file:
<?php chdir(dirname(__FILE__)); error_reporting(E_ALL | E_STRICT); require_once '../vendor/autoload.php'; use Daemoniser\DaemonAbstract; use Daemoniser\DaemonCommand; use Daemoniser\DaemonConfig; use Daemoniser\DaemonException; /** * Class ExampleOneDaemon */ class ExampleOneDaemon extends DaemonAbstract { public function run() { $this->logInfo((new DateTime())->format('Y-m-d H:i:s')); $this->logError('This logs an error This logs an error This logs an error This logs an error This logs an error This logs an error '); } /** * @return string */ public function randomAnimal() { $animals = ['Cat', 'Dog', 'Giraffe']; $this->echoLine($animals[array_rand($animals)]); } /** * @return DaemonCommand[] * @throws DaemonException */ protected function getAdditionalCommands() { return [ DaemonCommand::build('animal', 'Get a random animal.', 'randomAnimal') ]; } /** * @return bool */ protected function canImmediatelyStop() { return true; // Can we just kill the process? } } try { $daemon = new ExampleOneDaemon(); $config = new DaemonConfig( $daemon, '/path/to/error.log', '/path/to/info.log', '/path/to/mypid.pid', '/path/to/stops/filename.stop', 10, DaemonConfig::ONE_HUNDRED_MB, [ 'memory_limit' => '512MB', 'display_startup_errors' => 1, 'display_errors' => 1, ], [ 'www-data' ] ); $config->setMaxLogFileSize(1000); /** * Alternatively you can elect to set them via set methods. If you chose this route, you only need to explicitely * pass an instance of the daemon. */ // $config = new DaemonConfig($daemon); // $config->setErrorLogFilePath('/path/to/error.log'); // $config->setInfoLogFilePath('/path/to/info.log'); // $config->setPidFilePath('/path/to/pid/filename.pid'); // $config->setSoftStopFilePath('/path/to/stops/filename.stop'); // $config->setSleepBetweenRuns(10); // Sleep for 10 seconds between calls to `run()` // $config->setMaxLogFileSize(100000000); // Set the max log file size to 2,000,000 bytes before being rotated // $config->setIniSettings( // [ // 'memory_limit' => '512MB', // 'display_startup_errors' => 1, // 'display_errors' => 1, // ] // ); // $config->setWhitelistedUsers( // [ // 'www-data' // ] // ); $daemon->execute( $config, $argv ); } catch (DaemonException $e) { echo ' Exception executing command:' . PHP_EOL; echo sprintf(' Message: %s', $e->getMessage()) . PHP_EOL; echo sprintf(' File: %s', $e->getFile()) . PHP_EOL; echo sprintf(' Line: %s', $e->getLine()) . PHP_EOL; exit(1); }