Views : 1850  |
1 - IntroductionThe architecture we present here
is based on the 'plugin' , 'inversion of control', or 'dependency injection'
design patterns. If you want to know more about the theory, you will find it
here. Actually, after all this theory is thrown away, it becomes quite
simple :
- it provides a way to extend a base software.
- An extension is called a plugin.
- In order to communicate with the base software, a plugin
must implement a given interface.
- This interface acts as a contract between the base software and the
plugin, and is generally defined as an abstract class extended by the plugin's
code.
Such an architecture is very common. It is used by Apache, PHP, Firefox... Most software accepting
third-party extensions use this pattern. As a package format, PHK is a
tool of choice to implement a plugin architecture, as it is much easier to
manage a plugin as a single file, rather than as a set of files. So, in order to
make it still easier, PHK
provides a mechanism specifically intended for plugins. If we use our
old-fashioned term of 'entry point', we can say that, typically, a library does not define
any entry point, a program defines one entry point, and a plugin defines
a whole set of
entry points. In PHK-compliant plugins, these entry points are implemented as methods in a
given class. The methods' names are generally defined by an abstract class (or
by an interface). [:Note] In a typical configuration, a PHK archive used as a plugin is always included from
another script, and never directly executed, either in CLI or web mode.
How it works : when a PHK archive is included, if the 'plugin_class' option is
defined, an instance of this class is automatically created. The calling code
can then get this object using the PHK::plugin() method. 2 - Reserved methods
By convention, in a plugin, the method names starting with an underscore are
reserved for PHK. So, when implementing a plugin system, you must avoid such
names for your own methods. 2.1 - ConstructorThe class constructor
(__construct) receives the PHK package's mount point as argument. 2.2 - The _webinfo() methodThis method is
optional. When the webinfo 'Info' page is displayed, or when the CLI
'@techinfo' command is run, PHK looks for a '_webinfo()' method in the package's
plugin (if it is defined). If this method exists, it is called and must
display some information about the plugin. Prototype :
void _webinfo(boolean $html); |
where the $html argument informs the method
wether it must display its result as HTML or plain text. 3 - A basic exampleFirst, we define a plugin
interface. This interface contains only one method, a sort of output filter supposed to display a
string. Note that there is no limit to the number of methods a
plugin can provide. Here is the corresponding abstract class :
abstract class DisplayPlugin
{
public function display($string);
} |
Now, we know that every plugin must define this method in a class
derived from this base class.
Here is such a class : class UpperDisplay extends DisplayPlugin
{
//-- Display the string in uppercase
public function display($string)
{
echo strtoupper($string)."\n";
}
//-- Information
public function _webinfo($html)
{
$text='This plugin implements an all-uppercase display plugin.';
if ($html) echo "<p>$text</p>";
else echo "$text\n";
}
} |
Before building the archive, we need to specify which class
implements the plugin interface. This is done with the following option :
'plugin_class' => 'UpperDisplay' |
Then, we build the plugin
package. It only contains the class file and defines the plugin_class option.
Here is a basic PSF to generate the package : add /&
$(PSF_DIR)/DisplayPlugin.php
%options
return array(
'name' => 'Upper Display plugin',
'plugin_class' => 'UpperDisplay'); |
Now, in the base
software, we load a plugin and use it to display a string :
class Display
{
private $style=null; // DisplayPlugin object
public function set_display_style($name)
{
$filename=PLUGIN_DIR.DIRECTORY_SEPARATOR.$name.'Display.phk');
$mnt=require($filename);
$style=PHK_Mgr::instance($mnt)->plugin();
if (!($style instanceof DisplayPlugin)) {
PHK_Mgr::umount($mnt);
throw new Exception($filename.':
Invalid plugin');
}
$this->style=$style;
}
//-- This function displays a given string with the current style
function display($string)
{
if (is_null($this->style)) throw new Exception('no defined style');
$this->style->display($string);
}
} |
When creating an instance of the plugin class, the
corresponding PHP source is transparently loaded by the autoloader. If the
autoloader was not present, the class name contained in the 'plugin_class' option
would not be sufficient to locate the corresponding source code, and we would
have to set another option with the corresponding subfile's path.
The main code could look
like this : $output=new Display();
$output->set_display_style('Upper');
...
//-- This will display 'DO YOU HEAR ME ?'
$output->display('Do you hear me ?'); |
Through this example, you can see
how easy it is to implement a plugin mechanism in your software, using the PHK
built-in features. Implementing more complex plugin systems, similar to
what Apache and PHP implement, is just an extension of this basic example.
Last update : Saturday, 19 January 2008
|