Skip to content
Jason M edited this page Jun 5, 2017 · 5 revisions

Synopsis

The abstract Daemon class is the entry point for any daemon you want to create. It provides a Fluent Interface to allow for easier configuration and is a Singleton so only one daemon instance can be used in an application.

User code only has to implement the execute method in order to have a fully working Daemon. Below is an example of a bare bones daemon. You can run this snippet in the root of your application (where composer.json is).

Pressing ^C will exit the daemon.

require __DIR__ . '/vendor/autoload.php';

// needed for signal handling (so ^C will be captured and exit the daemon)
declare(ticks = 1);

class MyDaemon extends \Lifo\Daemon\Daemon
{
    // execute will be called every loop cycle (1 second by default)
    protected function execute()
    {
        $this->log("Daemon Loop %d", $this->getLoopIterations());
    }
}

// run the daemon (`setVerbose` is called so we can see something for the test)
MyDaemon::getInstance()->setVerbose(true)->run();

Expected output from the above example daemon (The header row below was manually added to explain what each column is and you won't see that in the output of the daemon):

                          PARENT   CHILD
TIMESTAMP                 PID      PID   MESSAGE
2017-05-30 15:04:44.7415: 5453     5453: Daemon Loop 1
2017-05-30 15:04:45.7414: 5453     5453: Daemon Loop 2
2017-05-30 15:04:46.7414: 5453     5453: Daemon Loop 3

The output above shows two different PID's. This is because the main daemon process (the parent) and any children Workers or Tasks can also output to the log. When a child emits a log message it's PID will be different than of the parent. This makes it easier to determine what is doing what in a multi-processing daemon.

Configuration

The Daemon has many configuration methods available to change it's behavior. A Fluent Interface makes it easier for you to configure your daemon before calling run(). Some examples (not an exhaustive list):

  • setDaemonize(true) - Toggle "daemon" mode (fork into the background) for the main process.
  • setLoopInterval(x) - Change the loop interval.
  • setLogFile('daemon.log') - Set log destination.
  • setAutoRestartInterval(3600) - Set auto-restart.
  • setDebug(true) - Enable debugging mode.
  • setDebugLevel(3) - Set debugging level.
  • ... etc ...

Any custom configuration you want to set must be done before you call the run() method.

Daemon Startup Order

When the run() method is called on your daemon a couple of things happen before the main daemon Event Loop actually starts.

  1. Daemon::validateEnvironment() is called. The environment is checked for dependencies. Such as, making sure the PCNTL extension is available, etc.
  2. If Daemon::$daemonize is TRUE then the daemon will fork into the background.
  3. Daemon::setupSignals() is called. The daemon will now catch all available signals. User-land code should never call pcntl_signal() to catch signals. Instead, you should listen for the DaemonEvents::ON_SIGNAL event by calling Daemon::on(DaemonEvents::ON_SIGNAL, callable).
  4. Daemon::initialize() is called (by default it does nothing). Your daemon can optionally override this method to add your own custom initialization. This is where you would setup any Plugins, Workers or Event handlers. Or, any other initialization that is required for your daemon.
  5. DaemonEvent::ON_INIT event is dispatched. This event mainly allows Plugins to do their own initialization when the daemon is initialized. User-land code can also listen for this event and do extra initialization, but there's no reason to do that since you can override the initialize() method.
  6. The main Event Loop starts up and your execute() will get called every loop cycle.
Clone this wiki locally