It’s has been a while since I post anything useful or technical. My last posts all have ben sponsored by some one!

When I was writing the new version of phMagick I had a few requirements in mind

It should be:

Having one big class would defeat the last 2 requirements, it would be too costly to manage contributions, and obviously there is no way to include just the features needed!

I tried some workarounds to php lack of multi inheritance support, but the code was just becoming to messy!

I come up with a solutions to “extend” a class by using a plugin system.

This way it is easy to include only the features you need and managing contributions is easy also!

The trick is relying on some magic functions, I’m not a support of magic in the code as I think it makes it more error prone and harder to debug and test, but in this case there is really no magic in those functions as they are all documented.

A closer look to PHP method overloading documentation pointed me to the __call()  and call_user_func_array() functions.

When the class is instantiated the constructor loads all the plugins found and stores them in a array.

When a method not present in the class is called PHP automatically runs the __call() that’s where I can see if the called function is available in any plugin, if so I just execute it.

 

The master class code

<?php
class Master
{
    //store available plugin classes
    private $plugins = array();
    //store methods found on each plugin
    private $methods = array();
    function __construct()
    {
        $this->loadPlugins();
    }
    /**
     *
     * this functio will load all available plugin into memory
     * Rules:
     * 		plugin is store nin ./plugins
     * 		plugin class is named plugin_>:bla>
     * 		php file is named >bla>.php
     */
    private function loadPlugins()
    {
        $base = realpath(dirname(__FILE__)) . "/plugins";
        $plugins = glob($base . "/*.php";);
        foreach($plugins as $plugin){
            include_once $plugin ;
            $name = basename($plugin, ".php";);
            $className = "plugin_".$name ;
            //create the plugin object so it can be stores and called later
            $obj = new $className();
            $this->plugins[$name] = $obj ;
            //store all methods found in the plugin
            foreach (get_class_methods($obj) as $method )
                 $this->methods[$method] = $name ;
        }
    }
    /**
     *
     * Run the plugin method
     * @param strint $method
     * @param string $args
     *
     */
    public function __call($method, $args){
        if(! key_exists($method, $this->methods))
           throw new Exception ("Call to undefined method : " . $method);
           array_unshift($args, $this);
           return call_user_func_array(array($this->plugins[$this->methods[$method]], $method), $args);
    }
}

The plugin class code, save it as ./plugins/hello.php

class plugin_hello
{
function hello_world()
{
echo "hello";
}
}

 

How to run it

$m = new Master();
$m->hello_world();

 

Do you know other way to accomplish the same?

Share your thoughts, I'm looking forward.