A few years back, I was sitting in c-base after the 27th Chaos Communication Congress had ended. I decided to quickly hack up a decent SubPageList extension for MediaWiki on top of a declarative parameter processing library I had created not much earlier, then titled Validator.
Earlier this year I decided to try out some dependency management approaches (buzzword: dependency injection) in this SubPageList extension. Since there was a lot of resistance from various sources to doing this, I had refrained from fully embracing this approach. Here I could go all the way, and see if it worked out nicely, or if it would, like critics predicted, make everything so complicated that it’d no longer be understandable. I was also determined to get rid of Single Responsibility Principle violations and to abstract away from MediaWiki interfaces to avoid tight coupling, especially from certain poorly designed classes. This also helped to decrease the chance of compatibility breaks.
In the process of refactoring this extension, I ran into usage of the ParserHook class provided by Validator (or rather ParamProcessor, as it is currently called). The idea behind this class was to have a derivative per parser hook that would define the parameters in a declarative fashion and process them using the ParamProcessor functionality before handling them to the actual parser hook handling code. While the idea is great in itself, the implementation of the ParserHook class is very poor. It violates at least half a dozen or so important design principles. Luckily I’m the author of this code, so I have the option to redesign it from ground up and then ditch the evil old version.
I’ve also been working on refactoring the ParamProcessor library and had already identified the ParserHook class both as badly designed and misplaced in the ParamProcessor library. I had thus detracted it already. Hence it was pretty clear that for a fresh implementation, the creation of a new component was in order. This is how the ParserHooks library was born.
Library structure and usage
The declarative OOP interface provided by this library allows you to define the signatures of your parser hooks and the handlers for them separately. The library makes use of the parameters specified in this definition to do parameter processing via the ParamProcessor library. This means that the handler you write for your parser function will not need to care about what the name of the parser function is, or how the parameters for it should be processed. It has a “sizes” parameter that takes an array of positive integers? Your handler will always get an actual PHP array of integer without needing to do any parsing, validation, defaulting, etc.
An instance of the HookDefinition class represents the signature of a parser hook. It defines the name of the parser hook and the parameters (including their types, default values, etc) it accepts. It does not define any behaviour, and is thus purely declarative. Instances of this class are used in handling of actual parser hooks, though can also be used in other contexts. For instance, you can feed these definitions to a tool that generates parser hook documentation based on them.
The parameter definitions are ParamProcessor\ParamDefinition objects. See the ParamProcessor documentation on how to specify these.
The actual behaviour for your parser hook is implemented in an implementation of HookHandler. These implementations have a handle method which gets a Parser and a ParamProcssor\ProcessingResult, which is supposed to return a string.
Knitting it all together
This library also provides two additional classes, FunctionRunner, and HookRegistrant. The former takes care of invoking the ParamProcessor library based on a HookDefinition. The later takes care of registering the parser hooks defined by your HookDefinition objects to a MediaWiki Parser object.
$awesomeHookDefinition = new HookDefinition( 'awesome', array( /* ... */ ) );
$anotherHookDefinition = new HookDefinition( 'another', array( /* ... */ ) );
$awesomeHookHandler = new AwesomeHookHandler( /* ... */ );
$anotherHookHandler = new AnotherHookHandler( /* ... */ );
$hookRegistrant = new HookRegistrant( $mediaWikiParser );
$hookRegistrant->registerFunction( new FunctionRunner( $awesomeHookDefinition, $awesomeHookHandler ) );
$hookRegistrant->registerFunction( new FunctionRunner( $anotherHookDefinition, $anotherHookHandler ) );
If you want to have the same hook, but with other default behaviour, you can avoid any kind of duplication by doing something as follows on top of the above code:
$hookRegistrant->registerFunction( new FunctionRunner( $extraAwesomeHookDefinition, $awesomeHookHandler ) );
Today I released version 1.0 of this ParserHooks library. This version is feature complete and stable. I do not expect to make much changes to it in the near future. Or at any point for that matter – it is not that much code after all, and it’s now doing what it is supposed to do. Currently the only consumer of this library is the SubPageList extension (or rather the 1.0.x branch of SubPageList, the code is not on master just yet). My future related work will mainly be in the form of implementations of the HookHandler interface in other components and on ParamProcessor, which after all provides much of the functionality that makes up this library.