Larahook: Hooks for Laravel
In SaaS or similar applications, there often comes a point when you want to start adding custom or tweaked functionality for clients without the codebase itself diverging. One of the most common solutions to this is to use hooks.
Hooks effectively allow you to make certain parts of your applications functionality open to modification by another part. My own usage of this for instance is allowing a single white label configuration file to adjust (in some cases quite deeply) how the larger application works. WordPress is probably the most well known for using this approach in order to allow its plugins and themes to easily interact with WordPress’s functionalty, without needing to make any changes WordPress’s own code.
To support this functionally in a Laravel application, for the last few years I have been using the wonderful esemve/Hook library by Bence Kádár. Unfortunately the library now appears to be mostly inactive – most critically lacking support for Laravel 8 (as well as PHP8 itself in some areas).
As such, with an growing requirement for additional functionality and updates, I decided to take the plunge and create a new maintained fork of the library.
Although for simple use cases coinvestor/larahook
can be used as a drop in replacement for the original esemve/hook
, I utilised the opportunity of it being a clean break to make some more involved changes to the libraries functionality.
The most important changes are listed below.
Laravel 8 and PHP 8 compatibility plus auto-discovery support.
The library has been updated to work with the latest version of Laraval, as well as to make use of the newer package auto-discovery features meaning you will no longer need to update your app.php directly.
Retired the initial content parameter, and replaced it with $useCallbackAsFirstListener.
In the original version of the library, you needed to specify both a call-back (to run when the hook was not being listened to) as well as optionally a default $output
value to be passed in as the 4th parameter to the get hook method. This lead to some confusing code where a hook would need to invoke the original call-back directly to get a default value (where output was null), but if a second hook had run previously, may instead include data in the $output the hook would need to be aware of.
To simplify this I changed the 4th parameter on get
to instead be a Boolean called $useCallbackAsFirstListener
, as such by setting this as true, the hooks default callback will always run and pass its value in as the $output
value for every subsequent listener. As such listener logic can be simplified to always expect to be working with the value of output. For now this option is left as false, as in the case where the default hook is taking an action (say sending an email) this behaviour would not be desired, and so for safety must be specifically enabled.
Listeners at the same hook at the same priority will no longer overwrite each other.
Unlike the original version of the library, lara-hook will allow multiple hooks to be registered on the same event at the same priority level. In the case this happens the hook listeners will be run in the order they are registered.
If you are making use of the original functionality where hooks at the same priority overwrite each other, code changes will be needed.
Support for falsely return values.
In our application there were a number of cases where we’d wanted a hook listener to return a falsey value back to an underlying function. This was previously not possible, as a hook returning false would trigger an abort – causing the hook to return the default value (rather than the falsey value itself).
This is no longer the case in larahook, meaning the Hook:stop();
method will now have to be used directly where a hook does need to abort, as 0/false/nulls will simply be returned from the hook like any other value.
New Methods: getListeners, removeListener and removeListeners.
Listeners can now be unregistered, both individually as well as all listeners for a specific hook.
Additionally a full list of listeners on a hook can be returned using the getListeners
method.
Test and bugfixes.
In addition to the functionality changes, I also spent some time adding unit tests and basic CI for the library. As is always the case when adding tests I managed to find and fix a number of minor bugs and edge cases across the library.
If you’d like to swap over to the new library, please have a look at our repo at https://github.com/CoInvestor/larahook/
Focus
Focus is a useful skill. One I often feel I lack. The more I should be doing one thing, the more my brain wants to do something totally different. For example, I decided to sit down and try and finish off a few more aspects of my main website: thybag.co.uk. Instead I ended up installing, learning how to theme and themeing this WordPress Blog and creating a small porfilo site around it. Although on the bright side, it does give me somewhere to complain about these things.
The focus issue at late seems to be even worse, I have exams at uni, which in most peoples mind would warrant me putting my focus in to revising for them. Well, not me. The knowledge i should be doing revision, combines with my natural procrastination instincts to drive me to do, what is actually a pretty impressive amount of work, on something totally different and unrelated. The result this time being that I’ve started and made quite a big of progress in to developing and constructing a brand new open source project (Magic).
I originally managed to justify it to myself, by claiming it was obstensably revising for a dynamic web exam that was coming up, But thats now long gone and my developemnt pace hasn t slowed all to much. My justifcantion also seems signifcantly weeker concidering the upcoming exams are on interface design, C and algorthums.. But hey, in reality isnt writing this blog just futher procrstination? If thats the case, i must resign myself to the fact im doomed and by doing that successful judtify getting another revision of Magic out the door by tonight. Yay.