Event Listener (Observer) Pattern
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
See: Observer pattern on Wikipedia
The Flow
First, there will be many listeners(observers), we can attach them to a dispatcher object.
Now, all listeners listened this dispatcher, if some event triggered, this dispatcher will notify all listeners.
So, if any listener has the method which matched the event, dispatcher will call this method.
Start Using Event
Create an event object named onBeforeContentSave
, and set some arguments.
use Windwalker\Event\Event;
$event = new Event('onBeforeContentSave');
$content = new stdClass();
$event->setArgument('title', 'My content');
$event->setArgument('content', $content);
Create your listener:
use Windwalker\Event\EventInterface;
class ContentListener
{
public function onBeforeContentSave(EventInterface $event)
{
$event->getArgument('content')->title = $event->getArgument('title');
}
public function onAfterContentSave(EventInterface $event)
{
// Do something
}
}
Add listener to Dispatcher:
$dispatcher = \Windwalker\Ioc::getDispatcher();
// OR
$dispatcher = $container->get('dispatcher');
// Attach listener
$dispatcher->addListener(new ContentListener());
Then we trigger the event we created:
// Trigger the onBeforeContentSave
$dispatcher->triggerEvent($event);
// ContentListener::onBeforeContentSave will set title into $content object.
$content->title == 'My content';
If a method name in listener equals to event name, Dispatcher will run this method and inject Event into this method. Then we can do many things we want.
Array Access
Event can access like array:
// Set
$event['data'] = $data;
// Get
$data = $event['data'];
Listeners
There can be two types of listeners, using class or closure.
Class Listeners
Using class, just new an instance
$dispatcher->addListener(new ContentListener());
You may provides priority for every methods.
use Windwalker\Event\ListenerPriority;
// Add priorities
$dispatcher->addListener(
new ContentListener(),
array(
'onBeforeContentSave' => ListenerPriority::LOW,
'onAfterContentSave' => ListenerPriority::HIGH
)
);
// Or using an inner method to get all methods
$dispatcher->addListener(new ContentListener(), ContentListener::getPriorities());
Closure Listeners
If using closure, you must provide the priority and an event name to listen.
$dispatcher->addListener(
function (EventInterface $event)
{
// Do something
},
array('onContentSave' => ListenerPriority::NORMAL)
);
Or use listen()
method.
$dispatcher->listen('onContentSave', function (EventInterface $event)
{
// Do something
}, ListenerPriority::NORMAL);
Dispatcher
Trigger An Event Object
This is the most normal way to trigger an event.
$event = new Event('onFlowerBloom');
$event->setArgument('flower', 'sakura');
$dispatcher->triggerEvent($event);
Add arguments when triggering event, the arguments will merge with previous arguments you set.
$args = array(
'foo' => 'bar'
);
$dispatcher->triggerEvent($event, $args);
Add An Event Then Trigger It Later
We can add an event into Dispatcher, then use event name to raise it laster.
$event = new Event('onFlowerBloom');
$event->setArgument('flower', 'sakura');
$dispatcher->addEvent($event);
// Nothing happen
$dispatcher->triggerEvent('onFlowerBloom');
Trigger A New Event Instantly
We don't need create event first, just trigger a string as event name, Dispatcher will create an event instantly.
$args = array(
'foo' => 'bar'
);
$dispatcher->triggerEvent('onCloudMoving', $args);
Stopping Event
If you stop an event, the next listeners in the queue won't be called.
class ContentListener
{
public function onBeforeContentSave(EventInterface $event)
{
// Stopping the Event propagation.
$event->stop();
}
}
DispatcherAwareInterface and Trait
In PHP 5.4 or higher, you can use DispatcherAwareTrait
.
use Windwalker\Event\DispatcherAwareInterface;
use Windwalker\Event\DispatcherAwareTrait;
class Application implements DispatcherAwareInterface
{
use DispatcherAwareTrait;
// ...
}
Default Events
Core
- onAfterInitialise
- onAfterBoot
- onBeforeExecute
- onBeforeRouting
- onRouterBeforeRouteMatch
- onRouterAfterRouteMatch
- onAfterRouteMatched
- onAfterRouting
- onPackageBeforeExecute
- onPackageAfterExecute
- onAfterExecute
- onBeforeRespond
- onAfterRespond
- onBeforeRedirect
Controller
- onControllerBeforeExecute
- onControllerAfterExecute
View
- onViewAfterHandleData
- onViewBeforeRender
- onViewAfterRender
Renderer
- onRendererPrepareGlobals
Asset
- onAssetRenderStyles
- onAssetRenderScripts
User
- onLoadAuthenticationMethods
- onLoadAuthorisationPolicies
- onUserBeforeLogin
- onUserAuthorisation
- onUserAfterLogin
- onUserLoginFailure
- onUserBeforeLogout
- onUserAfterLogout
- onUserLogoutFailure
- onUserBeforeSave
- onUserSaveFailure
- onUserAfterSave
- onUserBeforeDelete
- onUserDeleteFailure
- onUseAfterDelete
Mailer
- onMailerAfterCreateMessage
- onMailerBeforeSend
If you found a typo or error, please help us improve this document.