Phlyty\App composes a Zend\EventManager\EventManager instance. This allows you to trigger events, and attach listeners to events. It also allows the application to trigger events – and for you as a developer to write listeners for those events.

To attach to an event, you simply call attach(); the first argument is the event name, the second is a valid PHP callable, usually a closure.

$events = $app->events();

$events->attach('do', function ($e) {
    echo "I've been triggered!";

$events->trigger('do'); // "I've been triggered!"

The EventManager allows you to specify the priority at which a listener is triggered. This allows you to order listeners – but, more importantly, it allows you to decide when a listener is triggered in relation to the default listeners. This is important when you consider that the application triggers a number of events; many of these have listeners registered by default in the application, at the default priority. This means:

  • If you register with a higher (positive) priority, the listener will be triggered earlier.
  • If you register with a lower (negative) priority, the listener will be triggered later.

The priority argument comes after the listener argument.

$events = $app->events();

$events->attach('do', function ($e) {
    echo "Default priority\n";
$events->attach('do', function ($e) {
    echo "Low priority\n";
}, -100);
$events->attach('do', function ($e) {
    echo "High priority\n";
}, 100);


/* output:
High priority
Default priority
Low priority

Defined Events

As noted previously, the application triggers several events, some of which have default handlers defined.

Triggered at the very beginning of run().
Triggered during routing. A default route listener is defined and registered with default priority.
Triggered when halt() is invoked.
Triggered if no route matches the current URL.
Triggered if a controller bound to a route cannot be invoked (usually because it’s not a valid callable).
Triggered when an exception is raised anywhere during run().
Triggered immediately prior to sending the response.

Use Cases

You may attach to any of these events in order to alter the application work flow.

Error Pages

As an example, if you wish to display a 404 page for your application, you might register a listener as follows:

$app->events()->attach('404', function ($e) {
    $app = $e->getTarget();

You could do similarly for 500 and 501 errors.


You could implement a quick-and-dirty caching layer using the “begin” and “finish” events.

// Assume we've instantiated $cache prior to this
$app->events()->attach('begin', function ($e) use ($cache) {
    $app  = $e->getTarget();
    $req  = $app->request();
    if (!$req->isGet()) {

    $url  = $req->getUriString();
    $data = $cache->get($url);
    if (!$data) {

}, 1000); // register at high priority

$app->events()->attach('finish', function ($e) use ($cache) {
    $app  = $e->getTarget();
    if (!$app->request()->isGet()) {
    if (!$app->response()->isOk()) {

    $url  = $app->request()->getUriString();
    $data = $app->response()->getContent();
    $cache->save($url, $data);
}, -1000); // register at low priority

The above would look for a cache entry matching the current URI, but only if we have a GET request. If a cache entry is found, we set the response content with the data, send it, and exit immediately.

Otherwise, when the request is finished, we check if we had a successful GET request, and, if so, save the response body into the cache using the current request URI.