Hook/Plugin System Architecture

Enigma

Well-known member
What's the best way to design a hook/plugin system? Everyone, chime in; I'd also like to hear what Kier & Mike are thinking.

Eval arbitrary code
You can allow running arbitrary code at hook locations, by evaling the code (or including a file) This is one of the most flexible solutions for plugin developers, since it allows you to do pretty much anything. Disadvantages: Uses eval and potentially pollutes the scope where the hook is.

Use "filter" functions
Another approach assumes that plugin developers usually need to modify the contents of certain variables. You create a plugin as a function, and that function is registered for the hook location you've chosen. Calling the hook then consists of calling all functions registered for that hook, while passing them references to the relevant variables for that hook (which variables are available for which hooks would have to be documented, or look in the code). In your function, you do stuff to the variable(s) and you're done. Or if the main code has an if branch based on a decision taken in the hook code, then the function can return a value. For each hook location, the system would pass pre-defined variables to the hook functions. Depending on what the code is doing at the hook location, the relevant variable(s) would be passed. Benefits: No eval, no polluting the global scope. Disadvantage: not quite as flexible as option #1, since you're not in the same scope as the parent code. But there are workarounds to that-- globalising variables, or pulling in instances of singleton classes. The difficulty would be accessing local variables in the parent scope, if they were not passed to the filter function. [Notes: see the MediaWiki hook system]

Use an API of sorts
Expose as many actions as possible via an API, and products/plugins would accomplish things via the API instead of directly modifying the internal code with their plugin. This is less flexible, but offers great resiliency to refactoring. The internal XF code could change completely, and as long as the API interface stays the same, the plugins would continue to work. When I say API here, I'm referring to a set of classes/functions that client code could call to accomplish things. Could be called an SDK too, I guess. A remote API (via HTTP) is a separate animal (which would also be nice).

Other stuff...
Should there be the option (or requirement) for file-based plugins/products so that the code can be included instead of evaled? Should products be designed in such a way that a product is a class and each plugin/hook code is a function in that class (to be called by a filter/hook location)?

Bringing it together
I'd say have an API for all the common stuff people want to do. Plugins/products would use the API for everything that they can. If they want to do things that aren't possible with the API, then have the "filter" functions throughout the code base in strategic locations. If a need is shown for injecting arbitrary code anywhere in the current scope, then consider the first option.
 
Another approach is through a completely OOP code. The plugin extends existing classes to implement new functionality.

Disadvantages: more complicated to the average programmer.
 
Another approach is through a completely OOP code. The plugin extends existing classes to implement new functionality.

Along those lines, I hope they have been very judicious with the private/final keywords, and very liberal with the protected keyword. :)

As for your suggestion, I like it but it also has the potential of causing a lot of duplicated code, depending on how it is set up. You extend a class, override a method, call the parent method and then run your extra code. That would work well if you want to run code at the beginning or end of methods, but what if you wanted to tweak something in the middle of the method? Or would you also have the built-in class methods calling a bunch of dummy (no-op) methods in strategic places, so that the extended class could then define those methods and do things in them? I'd like to hear more if you have any specific ideas.
 
Enigma everything you said ...

God would do addons and plugins with one click ...

this would include full installation and billing you where XF gets their cut from an approved app and the addon, plugin, skin developer etc gets their cut. :D
 
Kier told in another thread that he will discuss the plugin and style features in a separate thread over the "Have you seen?" forum and he also said that we will not be disapointed!

I will wait to see what he has to say!
 
Relevant:
Plugins (well, "code event listeners") don't use eval. You specify a class+method and that is (auto)loaded and your code is run. The listener is given a specific set of arguments, similar to MediaWiki. This actually makes it documentable, and allows more backwards compatibility or at least documentation of potential issues.
 
FILTER FUNCTIONS:

This would be the ideal approach to extending the framework. The eval just doesn't feel right for reasons you have already mentioned. Having used both: the evil eval in vB plugins, and the Event Notification[1] system of symfony framework[2]; I find the latter a lot more powerful in terms of ease of development and code reuse.

You could have a single event listener connected/listening to multiple events, or combine multiple related event listeners into a single class for easier management. And since the code is stored in actual php files, opcode cache like XCache can work on it too. (which is not the case currently with eval).[3]

API & SDK:

When I say API here, I'm referring to a set of classes/functions that client code could call to accomplish things.

That; and if it consists of a consistent library which encapsulates tasks like user authentication & authorization, forum settings (including routing) administration, etc. it would be a godsend.

And I'm not really sure if this would even be needed (since we know almost nothing about the source code): Relying on well thought-out interfaces[4] instead of concrete classes' names for type-hinting could also help in maintaining that extra backward compatibility with plugins if and when things get refactored at a later stage.

[1]: Chapter 17 - Extending Symfony
[2]: symfony | Web PHP Framework
[3]: Compiled Code For eval() - XCache
[4]: PHP: Object Interfaces - Manual

Relevant:
That's exactly what I was anticipating. :)
 
Mike already said that the the hook system is basically an observable interface that allows you to attach custom methods to hooks; what concerns me is scoping issues, will you have to globalize default variables to manipulate them? Or perhaps you just need to pass a registry class to your method...I dunno! but am excited to see what's in store!
 
Top Bottom