Hooks and Events
CMS Made Simple provides numerous methods of calling your code when different things happen in the CMSMS core, or in other third party modules. Two of these methods are called "Hooks" and "Events". These tools allow you to do various things, including modifying data besides when your code is explicitly called via the admin console or a module tag. This document describes how to use both systems.
Note: The Hooks system was introduced in CMSMS 2.2. The Events system has existed since CMSMS 1.0.
What are the differences between Hooks and Events.
- Hooks are very light weight, and easy to both use and trigger within a module.
- Events are stored in the database, and as such must be explicitly created and destroyed.
- Hooks do not need to be pre-created.
- Hook handlers use the standard php "callable" mechanism. i.e: a class and method name, or a closure.
- Event handlers are stored in the database.
- Events are tied to a module, and CMSMS will attempt to load the module before calling the handler (except Core, which is special)
- Hook handlers assume that the class specified in the handler is already loaded and instantiated... or can be auto-loaded via the autoload mechanism.
- User Defined Tags (UDTs) can be used for event handlers.
- There are numerous required steps for modules that create or handle events.
- Modules cannot handle events that they originate... but modules can handle hooks that they "do".
- Hook handlers support a variety of argument formats (variable arguments). Events support only a single mixed argument, which is normally an array.
Questions and Answers
- What hooks exist, and where can I find out about them?
The reference to all core hooks can be found at LINK
- What events exist, and where can I find out about them?
You can see a list of the already-created events that your install of CMSMS knows about by visiting the "Extensions » Event Manager" page in the CMSMS admin console.
- Can hooks and events be combined?
Yes. The
\CMSMS\HookManager::do_hook()
If the hook called matches an existing event, i.e: ModuleName::EventName, then the system will "send" the event amongst all of the other handlers. - Are there events for all of the hooks?
No. Many of the new hooks in the CMSMS core for versions 2.2 and beyond do not exist as events.
- When should I create event handlers, vs. just using hooks.
- If your module is a light weight, lazy-loaded module and you only need the module to be loaded when it is explicitly called. However, if your module class file is small, it is probably better to just disable lazy loading, and forgoe using event handlers.
- When you need to do something very small, and very simple, and can write a short user defined tag.
- What are some examples of handling core hooks?
- Send an email when an admin login fails.
- Minify the HTML output code on page render
- Inject javascript into the head sectionof a page on render.
- Allow loose coupling so that your code can do various things without direct reliance on specific core objects.
- Can hook handlers be used to modify data?
Yes. Some hooks contain references to the data objects related to the hook. If the data is a reference, then you can modify the data in the hook handler.
Note: In most cases, this is a bad idea, and you must use extreme caution.
Handling Hooks
Assuming you have a CMSMS module entitled MyComments that has a front end form to allow users to submit comments, and that those comments are being nicely saved into the database. We want to do various things after a new comment is saved.
Note: let us assume that you have created a simple CMSMS module called MyComments, and that it's various classes exist in a namespace called MyComments.
- Using a simple callback to handle a hook on the frontend:
class MyComments extends \CMSModule {
public function InitializeFrontend() {
\CMSMS\HookManager::add_hook('newcomment',function($comment_text,$comment_author,$comment_ip) {
audit('','New Comment Created',$comment_author);
});
}
} - Use a static method in the module to handle the newcomment hook:
class MyComments extends \CMSModule {
public function InitializeFrontend() {
\CMSMS\HookManager::add_hook('newcomment','MyComments::NewComment');
}
public static function NewComment($comment_text,$comment_author,$comment_ip) {
audit('','New Comment Created',$comment_author);
}
} - Use a module method to handle the necomment hook:
class MyComments extends \CMSModule {
public function InitializeFrontend() {
\CMSMS\HookManager::add_hook('newcomment', [ $this, 'MyComments' ] );
}
public function NewComment($comment_text,$comment_author,$comment_ip) {
audit('','New Comment Created',$comment_author);
}
}
Using (sending) Hooks
The process to send a hook is very trival. it is done with the \CMSMS\HookManager::do_hook()
method. i.e:
$author = 'some_user@domain.com';
$ip = '192.168.1.100';
\CMSMS\HookManager::do_hook('newcomment',$text,$author,$ip);
Triggering hooks can be done from within class methods, or within module actions, or even within user defined tags.
Handling Events
Handling events within a module is a 4 step process:
- Return true for the HandlesEvents() method for the specific event(s)
class MyComments extends CMSModule {
public function HandlesEvents()
return true;
}
} - Register the Event Handler within your install routine
Because Events are stored in the database, and so are the handlers, you must tell the system that your module handles certain events from within the module's method.install.php.
Note: However, your module cannot handle events that it originates. So in this case our example is broken. For the purposes of completeness, We'll register a handler for the LoginFailed event.
$this->AddEventHandler( 'Core', 'LoginFailed' ); - Un-register the Event handler within your unintall routine
$this->RemoveEventHandler( 'Core', 'LoginFailed' );
- Handle the event
Events are handled by creating an event.<module>.<eventname>.php file in your module directory. In this case the file would be event.Core.LoginFailed.php. The parameters passed when the event was "sent" are available in the $params array.
debug_to_log($params,'Admin Login Failed');
Creating and Managing Events
- Create the event in your install routine
Events are stored in the database. Therefore you must Create the event in your module's method.install.php:
$this->CreateEvent('newcomment'); - Remove the event within your uninstall routine
Since you created the event in the install routine, you must destroy it in your module's method.uninstall.php:
$this->RemoveEvent('newcomment'); - Add Documentation for the Event
The system needs to talk to your module to get documentation for your event. This is done by overriding the module's
GetEventHelp()
andGetEventDescription()
methods.class MyComments extends CMSModule {
public function GetEventDescription( $eventname ) {
if( $eventname == 'mycomments' ) return 'Called after a new comment is saved to the database';
}
public function GetEventHelp( $eventname ) {
if( $eventname == 'mycomments' ) return $this->Lang('help_for_mycomments');
}
} - Send the Event
Events can be sent from anywhere in your code. But must be passed a single parameter, which is usually an array.
\Events::SendEvent('mycomments', [ 'text'=>$text, 'author'=>$author, 'ip'=>$ip_address ] );
Conclusion
CMSMS provides some very powerful methods of calling your code from various locations, providing extensible code, and hooking into various functionality of the CMSMS core and other modules. However, the newer "Hooks" system is a much easier to use, lighter, and more flexible mechanism for module developers.