Q & A - gregtampa/PHP-Daemon GitHub Wiki

PHP Daemon Implementation Q&A

Versions: 1.1, 2.0

What Do I Do First?

Your first step should be a walk through the applications in the Examples directory. They are contrived and simple but will give you a place to start.

Creating Your Application Structure

You can start by creating a new directory for your application then copying the config.php, error_handlers.php and run.php from any example app. While there's nothing in these files that is required by the library, this is the quickest way to get to a running prototype. Later you can integrate your own bootstrap, autoloader and error handling code if you're so inclined.

Next, create a new application class. This class will extend Core_Daemon. If you're building a simple application, it could be entirely implemented in this main class. For more complex applications, this could merely become the trunk of your application, providing the infrastructure and access to the Core_Daemon API.

Set protected/public variables

The Core_Daemon base class is abstract: You will need to implement a few protected class properties and methods in your application class. The PHP Simple Daemon idiom is to set these in your class definition. Your daemon class does not need and should not implement a __construct method.

  1. protected $loop_interval = 2.5;

    If you're building a server or using a blocking api in your event loop like socket_listen or LibEvent, you should omit this.

    How often (in seconds) should your execute() method be called. For example, you could have it run every second, or every 5 seconds, or every 60 seconds, etc. If your execute() method takes longer than $loop_interval to return, an error will be logged.

  2. protected $idle_probability = 0.1;

    If you're building a timer-based daemon using a $loop_interval, you should omit this.

    The probability (from 0.0 to 1.0) that garbage collection and other ON_IDLE tasks will run during this iteration of the event loop.

  3. protected $auto_restart_interval = 60 * 60 * 2;

    How often (in seconds) should the application restart itself? This will only be activated if you run in daemon mode (-d at the commandline.) If you omit, the application will restart every 8 hours.

  4. protected $install_instructions = array();

    This is an array of plain-text instructions that will be displayed when you pass -i at the command prompt. Gives administrators and other users the information they may need to run your daemon. Optional, but encouraged.

     protected $install_instructions = array (
                   'Create /var/log/mydaemon directory with 0755 with write access for the application',
                   'Setup sendmail'
                   'Update config.ini with production database credentials'
               );
    

Create a setup_plugins() Method

  • Use this method to setup any plugins. Optional, but most often this will at least be used to implement a Lock plugin. You can choose one of the Lock providers in the Core/Lock directory or write your own that extends the Core_Lock_Lock class. They are used to prevent more than one instance of your application from running at any given time.

  • Writing your own plugins is a fantastic way to create modular and reusable code and is very simple with the PHP Simple Daemon API. And don't forget pull requests: We will gladly accept pull requests for any non-proprietary plugins.

Create a setup_workers() Method

  • Optional, omit this method if you're not using the Worker API in your application.
  • Use this method to setup any workers. See related documentation for more information.

Create a setup() Method

  • Required. Implement any run-once functionality.
  • If your application uses database connections, listens to message queues, etc, this is where you should set that all up.
  • Setup any event handlers using the on() method.
  • (Upgrade Note: In v2.0, this method is not called in background processes and using $this->is_parent() is no longer necessary or encouraged)

Create an execute() Method

  • The actual work of your application begins here. In most of our production daemons, we use many classes and libraries, and the execute() method is just a few lines to bootstrap and run them.

  • You can implement all of your event loop functionality in the execute() method, or you could keep it very sparse and instead build an application that uses classes and components that are passed-in as callbacks to the Core_Daemon::ON_PREEXECUTE and Core_Daemon::ON_POSTEXECUTE events using the on() method. You can call on() multiple times, creating multiple callbacks for any given event. The on() method implements a simple first-in-first-called queue.

  • NOTE: If you've developed applications like this before, you know that you need a while(true) event loop. This is why your application stays running. You stop it by interrupting the loop with a break statement or kill signal. Remember that PHP Simple Daemon implements the event loop for you when you call run(). Your execute() method is called inside this event loop.

  • If you set $loop_interval=0 or omit it altogether, your daemon will sleep for just a fraction of a second in between execute() calls to manage events and defer to the Kernel scheduler. You should use this technique if you're creating a server and using a blocking API like listening on a socket, waiting for user input, or using LibEvent. Creating an event loop without a $loop_interval and without using a blocking API, you should expect your event loop to iterate very fast and for CPU usage to run at unacceptable levels.

How Can I Control the Event Loop?

You have several options to control/break the event loop from your application code.

  • Graceful Shutdown. When a ctrl+c or kill [pid] signal is sent to your application, it will do a graceful shutdown: It will finish the current iteration of the while() loop, run its destructors, run teardown() on all loaded plugins, release the lock, wait for any worker processes to exit, and exit cleanly. Any running processes using the Task API will continue running until their task is complete and then exit cleanly. You can also initiate this in your code by calling $this->shutdown(true).

  • Exception. You can throw an exception from within the event loop that will break the loop and trigger a fatal error. In daemon-mode, your application will attempt to recover by restarting itself.

  • You can manipulate the loop frequency. You can change the $loop_interval while the daemon is running using the loop_interval() method.