Creating and Using Plugins

tofek-nexway edited this page May 5, 2017 · 8 revisions

Creating and Using Plugins

Version 2.0

Plugins: What and Why

PHP Simple Daemon support for very lightweight plugins was introduced in v1.1 and changes were made in v2.0 to standardize interface names (the name of the interface changed but the contract didn't) and instantiation.

Plugins provide 2 advantages over just using some random class:

  1. They provide a basic level of code reuse that, being specified by the library, will be universally translatable across all Simple Daemon apps. They self-contain their own setup() and ensure, during daemon startup, that all dependencies and requirements are satisfied. That's far preferred to reusing a class and having a random failure hours after the daemon starts because an unknown dependency wasn't loaded.

  2. Most importantly, code in plugins is special. They are loaded early in the startup order, are automatically garbage-collected by the daemon, and are automatically removed in child processes like forks and workers.

Creating Plugins

Creating plugins really is as simple as implementing the Core_IPlugin Interface. We'll go through each method in the order they'll be called in a plugin's lifecycle.

  • __construct()

Your plugins do not have to implement a constructor, but doing so can be useful: When Core_Daemon::plugin() is used to create a plugin object, a reference to the daemon is passed, as well as any additional arguments that may have been passed to the plugin() method.

  • check_environment()

This method is called during the startup process. It's expected to ensure that any dependencies are satisfied. Each plugin and worker should ensure that its own requirements are met.

  • setup()

The plugin setup method is called before worker or daemon setup routines to ensure that all plugin functionality is available to those methods. Use a plugin's setup() method to connect to external resources like databases and file descriptors, and to "attach" the plugin into your application by setting callbacks using the $daemon->on() method.

  • teardown()

Plugin teardown is called during application shutdown. It's important to use the teardown method and not a conventional __destruct() method in your plugin class. During restarts the daemon may run teardowns on all of it's objects to free resources, but wait in a standby-like state for any background workers to finish. It's important for all resources to be freed during this state.

Using Plugins

The Core_Daemon API includes a plugin loader to make it very easy to begin using a plugin.

  • When using a plugin, it's important that you add a setup_plugins() method to your daemon class. Loading plugins elsewhere is not idiomatic and could cause unexpected problems.
  • In setup_plugins(), you can use the API to load plugins. See this excerpt from the PrimeNumbers example application:
    protected function setup_plugins()
    {
        $this->plugin('settings', new Core_Plugin_Ini());
        $this->settings->filename = BASE_PATH . '/settings.ini';
        $this->settings->required_sections = array('signals', 'default');
    }
  • As you can see, the plugin loader uses a simple dependency injection pattern. You supply an alias that you'll use to access the plugin and an instantiated plugin object.

  • Built-in plugins will be distributed in the Core/Plugin directory. For application-specific plugins you can create a similar Plugin directory inside your application.

  • When loading built-in plugins from Core/Plugin you can omit the 2nd parameter and even part of the class name. By just passing-in the significant part of the filename the plugin loader will find and instantiate the plugin.

    For example, to load the Ini plugin, both of the following examples are identical:

    $this->plugin('ini');
    $this->plugin('ini', new Core_Plugins_Ini());
    
  • The second, longer-form, however, is useful if you want to load 2 instances of the ini plugin. You'll need to supply unique aliases for each instance of the plugin.

    An example of using two INI files in the same plugin:

    $this->plugin('credentials', new Core_Plugins_Ini());
    $this->plugin('settings', new Core_Plugins_Ini());
    $this->credentials->filename = '~/prod/credentials.ini';
    $this->settings->filename = BASE_PATH . '/MyDaemon/settings.ini';
    echo $this->credentials['mysql']['user']; // Echo the 'user' key in the 'mysql' section
    
  • It's important to understand that plugins and workers are both created as public instance variables (properties) so aliases must be unique among plugins and workers and cannot overwrite any existing properties in either Core_Daemon or your application class.

  • If you're using a Lock feature to prevent multiple instances of your daemon from running, note that all of the Lock providers are also plugins and they should also be loaded in the setup_plugins() method.

    There is one important difference between standard plugins and lock provider plugins: If you use the "short form" to instantiate the plugin and omit the 2nd parameter, the alias must include the Lock_ prefix. For example:

    $this->plugin('Lock_File'); // Instantiated at $this->Lock_File

    If you omitted the Lock_ part of the alias name, the plugin loader would look for a File plugin in Core/Plugins/File.php instead of the Core_Lock_File class in Core/Lock/File.php