08

In thinking about FluxBB 2.0, one of the important goals is to come up with a well designed and easy to use extension system. The days of manually digging into the code are past, people now expect to be able to install an extension with one click.

In PunBB 1.3 (which we forked into FluxBB 1.3 Legacy), Rickard introduced the use of hooks and eval, to allow extensions to insert code and extend the core software. Originally this system was designed to allow small changes, but by the time the software was finished it had mutated into a full blown extension system.

Along with the disadvantages inherent from a hook system (see below), FluxBB 1.3 Legacy used a rather horrible system of PHP code within XML code, which was parsed and inserted into the database on install. The aim was that then the install files could be deleted or overwritten and the extension wouldn’t be affected until it was uninstalled/updated. However due to the fact that most extensions referenced external files (be it included php files, or css and images) this theory did not work very well.

So, going back to the beginning, what are the different solutions available for writing extensible PHP software?

Hooks

Hooks are probably the most common way used to make PHP software extensible. The idea is fairly simple; throughout the code there are “hooks”, where extensions can insert code. Below is an example hook taken from FluxBB 1.3 Legacy:

if (isset($_POST['form_sent']))
{
	($hook = get_hook('po_form_submitted')) ? eval($hook) : null;
 
	// Make sure form_user is correct

In the above example the get_hook method will look for all the code which has been registered with the hook “po_form_submitted”, combine it and return it. The code will then be executed using the eval function.

The disadvantages of the hook system should be fairly obvious…

  • Blocks of code can be inserted, but only at pre defined locations within the software. This means extra thought has to be given during development as to where hooks may be required. If someone writing an extension needs a hook in a place where there isn’t one, then they are out of luck.
  • Insertion of code is all very well, but what if we want to change or remove some existing code? There is no way to do this using hooks. This leads to extensions with rather “interesting” algorithms to attempt to undo or block code in the core, which can be both messy and inefficient.

Despite these rather major sounding disadvantages, I would argue hooks are good! At least much better than requiring users to change code by hand…

In the example code given above the eval function was used to execute the inserted code, but where is the code actually stored, and is this the best way to execute it? There are a few options:

  • Store the code in a database or XML etc. then load and eval it when required. This probably isn’t the best solution as it involves storing code in the database or other storage that isn’t designed for code. Caching would probably also be required, loading and parsing a large XML file on every page load would not be efficient.
  • Keep the code for each hook in an individual PHP file and include it when required. This option seems reasonable, but could result in having a huge amount of tiny files being included, which could be hard to organize and may not be ideal if the server isn’t running any form of opcode cache.
  • Keep the code all in one PHP file and register specific functions with each hook, then call these when required. This seems like the best solution, but introducing functions introduces problems of variable scope. The underlying code would need to be written carefully in a way that appropriate variables can be passed to each hook.

Patches

A totally different approach to extending PHP software is patching it. In a similar way to how a patch file is used, the software could read instructions from an extension and physically modify the appropriate code.

@@ -58,7 +58,9 @@
 // Did someone just hit "Submit" or "Preview"?
 if (isset($_POST['form_sent']))
 {
-	// Make sure form_user is correct
+	// This is some new code
+	$username = isset($_POST['username']) ? $_POST['username'] : null;
+

This means that once the extension has been installed, there is no extension code left to worry about; the physical files have been changed and the extension installation files can be removed.

At first this may seem like a much better solution than hooks; code can be inserted/changed/deleted anywhere in any files, there is no need to litter hooks all over the place. However it has three major disadvantages:

  • Often in shared hosting environments PHP will be configured to run as a certain unix user (usually “www-data” or “nobody”). For the software to be able to patch itself, all files would require to be writeable by that user. This could prove to be a major security flaw as it would allow other users on the server to modify your files. A work around for this could be to modify the files over (S)FTP instead, but then the user would need to provide their hosting login details.
  • Updating the core software becomes harder. Since the files have been physically modified it is no longer a simple task to replace them with updated files. In theory core updates could be applied the same way extensions are installed (i.e. as patches), but with a modified board conflicts are fairly likely, and code that been removed by extensions is likely to just be replaced when the core is updated.
  • An extension with a bug, or even a malicious extension, could render the whole software unusable by deleting sections of code or inserting invalid code. Since the actual files would have physically been overwritten there would be no way to recover from this, other than the user manually uploading a backup (if they were organized enough to have one!).

To me this seems like a rather show stopping problem, what’s the point in a one-click install system if uninstallation can require manually removing the changes. The idea of a buggy extension being able to totally destroy a working install is not acceptable!

Conclusions

What will be use in FluxBB 2.0? Only time will tell, but I’m currently leaning towards making use of hooks again, but attaching functions loaded from native PHP scripts, rather than chunks of code to be evaled.

Have any suggestions or better ideas? Please leave a comment!

11 Comments

Add Your Comment
  1. I guess I should take responsibility for that horrible mess of eval, XML and PHP file caching that was the old extension system :) Whatever you guys decide to do, it can only get better. I would take a look at WordPress. It has a hooks based extension system that is very simple and yet seems to keep everyone pleased.

    google.com/accounts/o8… on Thu 22nd Apr '10 at 8:20am
  2. I’m Rickard by the way, not google.com/accounts/o8… :)

    google.com/accounts/o8… on Thu 22nd Apr '10 at 8:22am
  3. I like the idea with the special function calls best:
    * a whole extension is stored in one file without any xml or db usage
    * saving the registered function calls in an array allows on-the-fly disabling of some hooks and extensions
    * dynamic function calls with $name($parameter) have a good performance according to http://weierophinney.net/matthew/archives/121-Benchmarking-dynamic-functionmethod-calls.html

    damaxxed on Wed 5th May '10 at 11:25pm
  4. I’ve always wondered how exactly a strict-OO approach would work with multiple extensions. Obviously every extension class would overwrite some base class and not just the last extension class. Thus, it seems, it would not be possible to extend a class with multiple extensions (except for something like decorator or command patterns probably). How would that be done?

    Franz on Thu 6th May '10 at 12:25pm
  5. Nice to see you’re still keeping an eye on things Rickard. :)

    Franz, I might have missed your point somewhat, but either straight functions or just extending the respective core class with an extension class would both work fine, (in theory)?

    Matt on Fri 7th May '10 at 4:54pm
  6. Well, I don’t see how that would work with multiple extensions. You can’t just extend a class with multiple classes or extend an unknown (dynamic) class…

    I guess the way a complete OO approach would go would not be via subclasses (at least in most cases), but rather through stuff like the patterns mentioned in my last comment. I should take a look at it. Lacking the time though ;)

    Franz on Fri 7th May '10 at 8:42pm
  7. Personally, (I must be thinking along different lines from yourself regarding how extensions would work), I can’t see much of a problem whichever way it’s done. As to what the correct OO approach is, that’s a subjective point. Each will have their own opinion on what constitutes the correct or best method. Simplest is always best, IMHO, whichever that may be.

    Matt on Fri 7th May '10 at 9:50pm
  8. Well, how exactly would you imagine an extension overriding some core base class and another extension wanting to do the same? (O.k., it just appeared to me that this might be possible with a hook in __call()…)

    Franz on Fri 7th May '10 at 11:07pm
  9. Personally, I see extensions as providing additional functionality outside of the core classes, (extending only), hence overriding a core function shouldn’t be a consideration. However, if that behaviour is desired in certain places, add hooks where appropriate in the base class functions and let the hooks take precedence over the core functions, or add the ability for external classes to be loaded inplace of the base classes. There are so many different ways to approach each and every possibility, (without going the ‘pure OO’ path and throwing 10x the amount of code needed at everything). Simple approach logic and variation seem to take a backseat when people start thinking about OOP, in my opinion.

    Matt on Sat 8th May '10 at 3:47am
  10. I actually grew to like Frank’s idea (http://61924.nl/blog/e/00034-extending-php-software.html) now. In a way, it is the back-to-the-roots way, combining mods with auto-installation and hooks and probably the best solution when talking about performance. It is also safer than pure patching…

    Franz on Tue 18th May '10 at 2:39pm
  11. Dredging this topic up again, but what about not actually making any alterations to existing code for plugins, but rather make it so that each plugin is a separate entity completely? Install it and it takes precedence, remove it and the original is used. Granted, there would be code repetition in the plugin. but no hooks or suchlike would be needed. The template system would have an inbuilt method for adding extra fields in the page output, so that would take care of any minor additions to output necessary.

    For example, if there was a default directory where scripts are located for each valid requested page. When that page is requested, the respectively named file is required/included. There’s also a plugin directory too which is scanned first, however. If a file is found in that directory matching the requested page name, that takes precedence over the default file. In theory, it’s more like a modular approach than plugins per se, but it seems possibly the least hassle method for including additional functionality without bloating the code with hooks, or requiring users to alter the core code. Other than adding entries to the DB if necessary, it could be a completely isolated addition.

    Just a thought.

    Matt on Fri 3rd Dec '10 at 9:04pm

Your Comment