How to do plugin based development?

A event system based on the observer pattern (using SplSubject and SplObserver interfaces) could be suitable. All pluggable classes should implement SplSubject and plugin classes implement SplObserver. It is quite easy to implement…

This is both a common and a tricky question. Creating a plugin system that’s both easy to develop with, and powerful, is quite tricky. First, it may be helpful to get the obvious things out of the way.

First of all, a good plugin system relies on a good application design. It needs to be a before though, not an after thought. The second thing to know is that you DON’T want a plugin system that add/removes/replaces any existing code; a plugin system should allow developers to add, override, or remove functionality without having to actually modify any existing program files.

As for implementation, as already mentioned, hooks are very common and are almost unavoidable, but they have there down falls. First of all, hooks can make source code messy, as it becomes scattered with “hook” checks and so on. The second downfall is that relying on hooks means plugin developers can only modify functionality that you’ve specified with your hooks, hence the plugin system will be limiting, which will encourage developers to hack program code to achieve what they want.

One plugin method I’ve been considering, involves a few key design decisions rather than a plethora of hooks scattered through the applications. To start, you can have a filter system which applies functions to application input (request data) and application output (response data). On top of that, you would want a well designed object hierarchy which when combined with dependency injection, could allow developers to “extend” virtually any class they want - this will likely be faster and much more powerful than a hook-system. Another thing you want for your plugin system, is a good easy to use API. If a developer wants to retrieve the currently logged in user name, or pull data from the database, it should be made easy, so it’s essential your API, or rather framework, is intuitive and as simple as possible. Finally, you want to provide a way for developers to slot in whole blocks of code, such as modules. For example, if I wanted to make a shopping cart plugin for a blog, this would be an absolute requirement.

There’s a few things anyway worth considering for a plugin system. The most important thing to know is a good application architecture is critical to allowing flexible yet simple plugin development.

Just a thought:

If you have a factory, like this:


class Factory {
	
	protected $db;
	protected $conf;
	
	public function __construct(PDO $db, Config $conf) {
		$this->db = $db;
		$this->conf = $conf;
	}
	
	public function model($name) {
		$model = "Model_$name";
		return new $model($this->db, $this->config);
	}
	
	public function controller($name) {
		$controller = "Controller_$name";
		return new $controller($_POST, $_GET, $_SESSION);
	}
	
	public function helper($name) {
		//load helper...
	}
}

Then whenever you want a new instance of some class, you ask the factory to create it for you, and inject the dependencies.

Now lets say you have a MyFactory.php.dist file, that you have to copy and rename to MyFactory.php and which contains the following:


class MyFactory extends Factory {
		
}

Now, add your own method:


class MyFactory extends Factory {
	
	public function model($name) {
		if ($name == 'User') {
			return MyUserModel($this->db, $this->conf);
		} else {
			return parent::model($name);
		}
	}
}

With this setup, you can override any class in the system with your own. Your own class will typically extend from the one you override, and implement one or two methods to override the parent methods, thus altering the behaviour of those objects without changing the core system.

This will be useful for overriding default application behaviour from your plugins.

I haven’t actually used this in any project, though I’ve seen that Magento uses this method, only they’ve implemented it through config files.

oeyvind presents one of the examples I was referring to. You may notice though, that this will not work when two plugins want to modify the same class, either because only one plugin’s extension will be used, or because plugins will override methods added or modified by other plugins. So if you do use this method as is, you’d have to perform some checks when installing plugins to ensure that two plugins don’t try to override the same class; if you find they do, then only one should be allowed to be activated at a time.

This is another reason to use hooks. Hooks can allow unlimited plugins to work on a single “hook”. The trick is to design your application in such a way that only a few hooks are needed to perform the majority of tasks.

One of the best things you can do to accommodate plugins from an application design perspective, is to leverage the use of interfaces and the “services” concept. As an example, if I wanted to create a plugin which allowed error logging to be sent to a database instead of a text file, by simply registering my logging class (which would implement a “logger” interface) as another logger, the system should then allow logs to be sent to both the existing plain text file, and now also the database. So by designing your application framework around plug-and-play interfaces and the “services” concept, you can allow components to be easily switched on and off, and accommodate multiple components serving the same task. Dependency injection would play a key role in any such implementation.

As long as everything is managed through the factory, you should be fine:


class Factory {
  function new_PDO($registry) {
    return new PDO($this->username, $this->password, $this->database);
  }
  function new_SomeModelComponent($registry) {
    return new SomeModelComponent($registry->get('PDO'));
  }
  function new_FooPlugin($registry) {
    return new FooPlugin($registry->get('SomeModelComponent'));
  }
  function new_BarPlugin($registry) {
    return new BarPlugin($registry->get('SomeModelComponent'));
  }
}

Now, you want BarPlugin to use SomeOtherModelComponent. No problemo:


class MyFactory extends Factory {
  function new_SomeOtherModelComponent($registry) {
    return new SomeOtherModelComponent($registry->get('PDO'));
  }
  function new_BarPlugin($registry) {
    return new BarPlugin($registry->get('SomeOtherModelComponent'));
  }
}

Or did I completely miss your point?

My point is, what if FooPlugin and BarPlugin both want to extend “SomeModelComponent”? Say then we have a “BazPlugin” that relies on “SomeModelComponent”, what version of “SomeModelComponent” does he receive? The original SomeModelComponent? The FooPlugin extended version of SomeModelComponent, or the BarPlugin extended version?

If it relies on SomeModelComponent, than I would feed it that. If you find that there is duplicate functionality between SomeModelComponent and FooPlugin/BarPlugin, then perhaps class inheritance is the wrong way to go about it?

I see your point, wardrop; only one class can be loaded, so which plugin’s class is it gonna be?

I guess one have to implement some sort of filtering or chainloading to solve this problem.

This discussion has inspired me to work on a Service-Orientated PHP framework (I just made that name up by the way, so don’t go googling it). Basically, it’s a light framework which combines dependency injection with a service model. Some key points are…

  • All services are defined as interfaces, hence allowing unlimited implementations of a specific service and a level of abstraction which isn’t overly complex or confusing.
  • Because services can have one or more implementations, the framework allows a service to define whether only one of the implementations is to be used, or if all the implementations are to be used. If implementing a logger, you probably want multiple implementations to be used (one implementation logs to a database, one logs to a file, etc) where as with an authentication service, you only want one implementation to be used (which can be set at runtime depending on the authentication mode the user chose, for example).

It’s still in early stages, but I mention this because such a system would allow maximum flexibility and would be very extensible and hence plugin friendly, while at the same time not being overly complex; it’s a relatively simple concept consisting of the “Service Manager” (manages all services), the “Service Controller” (which manages all implementation and their initialisation) and of course the service implementations themselves.

I’m a nut for simplicity, so anything I create has to be simple to use for both end-users and developers; complexity should be introduced only where it’s absolutely required.

It’s likely no one will understand what I’ve just written, so don’t worry if you don’t :slight_smile: