24

Is there a consensus on how plugins should be implemented in a PHP application?

I've looked into the observer pattern which comes close, it's really just a notification system and doesn't allow code to extend the application directly. I'm currently using a simple hook systems that I came up with:

public function registerHook($hookName, array $params = array())
{
    $this->hooks[] = $hookName;

    foreach ( $this->plugins as $pluginName => $hooks ) {
        if ( in_array($hookName, $hooks) ) {
            $plugin = new $pluginName($this, $this->view, $this->controller);

            $plugin->{$hookName}($params);
        }
    }
}

This works well for my purposes but I'm curious if there's a design pattern out there that has been tested and proven many times over and I'm just re-inventing the wheel.

ThinkingStiff
  • 64,767
  • 30
  • 146
  • 239
Elbert Alias
  • 1,770
  • 1
  • 16
  • 25
  • I'd suggest having a look at how it works with Wordpress. I don't know myself, but I have a feeling it's using hooks, and BOY does wordpress have a ton of plugins. Good question, upvoted. – Relequestual Feb 11 '12 at 23:48
  • @Relequestual I'm familiar with WordPress, they take a procedural approach with "actions". http://codex.wordpress.org/Function_Reference/add_action – Elbert Alias Feb 12 '12 at 00:00
  • I'm going to say this question is not a dupe, but in some way it sort of is. Seems the consensus was discussed here http://stackoverflow.com/questions/42/best-way-to-allow-plugins-for-a-php-application – Relequestual Feb 12 '12 at 00:04
  • I read that but it's an old thread. PHP 5.3 has many OO features that can be leveraged. – Elbert Alias Feb 12 '12 at 00:21
  • With the observer pattern, you can/need to activate observers at the point where you want to be able to extend your code. I.e. you need to have designated trigger points at which you hit up your observers and have them tell you if there is anything else that you need to do. Definitely not the cleanst way of drop-in-code-have-it-do-the-rest. – Mbrevda Feb 12 '12 at 09:58
  • How about subclassing an abstract `plugin` class, or maybe an interface, that contains a number of methods (the hooks) that can be implemented/overloaded by a plugin (eg. enable, disable, on_certain_action)? An autoloader could handle the instantiation. Interesting question indeed. – leemeichin Feb 12 '12 at 14:01
  • @fuzzyDunlop That's almost exactly what I did, e.g. https://github.com/ElbertF/Swiftlet/blob/master/Swiftlet/Plugin.php and https://github.com/ElbertF/Swiftlet/blob/master/Swiftlet/Plugins/Example.php. – Elbert Alias Feb 12 '12 at 20:41
  • Maybe I'm wrong , but doesnt every good framework have a hooking system ? Are you using a framework? If not , maybe take a look at Code igniter core files and see how the hooking system works there. Simply allow and run some process in a key point in your system , thats how i see this implementation of plugins , i don't think theres a pattern – Tudor Feb 12 '12 at 22:28
  • @TudorTudor Not using a framework but creating one (yes, ironic for someone who doesn't want to reinvent the wheel.) It seems that every framework out there has it's own way of implementing plugins. – Elbert Alias Feb 12 '12 at 22:40
  • @ElbertF it's good that your makning your own framework , i made mine also , and inspired mostly on CI . Most of the PHP frameworks have flaws , and maybe you can better them . For me CI hooking system it's easy to read and understand . Idk how zend's hooking system is because it's a hell to look at theyr core files – Tudor Feb 12 '12 at 22:52
  • You might be interested on [these design patterns](https://stackoverflow.com/a/10913898/4970442) – Pablo Bianchi Oct 04 '19 at 02:24

6 Answers6

9

There is no consensus as in the Silver Bullet sense. For established patterns, you have multiple options like

to name a few.

Which you use is up to you, but you should make sure your system architecture supports the modularity. Have a look at these slides for some ideas

Gordon
  • 312,688
  • 75
  • 539
  • 559
  • @Gordon What do you think about decorating the main 'application' object (like silex has), doing your modifications to it, then returning it? Then you could repeatedly decorate these, adding more decorators for each new application modification, and being able to remove each one any time without affecting the rest? – Jimbo Aug 31 '14 at 22:12
  • @Jimbo Sure, if that's what you want to do. But keep in mind that a Decorator usually only operates on the existing API of the decorated class. You could also use [Silex `before()` and `after()` hooks](http://silex.sensiolabs.org/doc/middlewares.html). – Gordon Sep 01 '14 at 04:34
3

I think an Events Dispatcher is a nice and clean way to implement plugins, or any extension for that matter. An Events Dispatcher is an implementation of the Observer pattern, and is being used in symfony, Symfony2 and Zend Framework 2 (beta).

Looking through any of that source on github will make for some interesting reading. Though, an interesting bit of information can be found here:

http://components.symfony-project.org/event-dispatcher/trunk/book/02-Recipes

I wrote an Events and Hooks class a few years back for a project, I'll post that here if I can find it.

lshepstone
  • 136
  • 9
1

Take a look at the Yii framework. Yii heavily relies on events which are much cleaner than hooks, actions, etc. Using events, you can allow different parts of the system to talk to each other in an object oriented manner.

orourkedd
  • 6,201
  • 5
  • 43
  • 66
1

Zend Framework is using the dispatchLoopStartup() and dispatchLoopShutdown() hooks as class methods. Each plugin is a class that implements the aforementioned methods.

ZF manual reference

aletzo
  • 2,471
  • 1
  • 27
  • 31
1

The way you've done this, with hooks is also how I implement this.

The biggest problem with your sample though, is that your function instantiates the plugin. Why not pass an instance of the plugin instead?

The way I've done this, is that a plugin gets instantiated first, and registers its hooks itself.

Evert
  • 93,428
  • 18
  • 118
  • 189
  • A plugin may implement several hooks, creating a new instance for each avoids a shared state. – Elbert Alias Feb 13 '12 at 20:39
  • The shared state is something the plugin itself should be aware of. If you need separate states, use multiple objects. – Evert Feb 14 '12 at 01:31
  • Why is it an issue though? It keeps the hook implementations separated and there's no need to keep track of plugin instances. – Elbert Alias Feb 14 '12 at 04:59
  • You loose a lot of functionality that OOP gives. By doing this, you're effectively treating your objects as static. If you want to read more about this, look for 'Dependency injection'. Just general design patterns will also be of benefit. Even though this may not seem as an issue now, in the future I guarantee you'll see this is inflexible.. – Evert Feb 14 '12 at 09:24
0

Well, there's a link to the project called jin-plugin right in the Wikipedia article about Plugin concept. I am seeing this framework first time, too, but, maybe, you can use it right away.

Besides, you should really google for things like "Plugin Pattern", there's just two links I found on first page: Plug-in Pattern, Extensibility pattern (wikipedia).

If it's really a pattern, it should be language agnostic, so you can safely take any existing solution from any language and convert it to PHP.

P. S. Thanks for question, anyway, you really did raise my own interest in this topic. ;)

hijarian
  • 2,159
  • 1
  • 28
  • 34