Event Hooks

Observe app lifecycle events without touching framework code.

Overview

$app->on() registers a listener and $app->emit() fires an event. Listeners are observational only: they receive typed arguments, their return values are ignored, and there is no return false to block. A listener that wants to stop something throws or redirects, like any other guard.

// Register a listener
$app->on('app.route:after', function (Page $page) use ($app) {
    // observe the resolved page
});

// Emit an event (framework internals only)
$app->emit('app.route:after', $this->page);

Listeners run in registration order. Addons load before routing, so app.route:after is the earliest point where the resolved Page is available.

$app->onEmit(callable $observer) registers a separate observer, called after every listener with (string $event, string $source, float $ms). It exists for tooling such as a request timeline and is not for control flow.

Naming

Events are named domain.action:timing, such as page.update:after. The full string is matched exactly.

Events

Event When Args
app.request:before The first event of each request, before the page is resolved none
app.resolve:after The deepest matching page has resolved, before routing Page $page
app.route:after $page->route() has resolved the routed page Page $page
app.addons:after All addons have loaded none
app.exception A Throwable was caught while handling the request Throwable $e, int $code
app.shutdown Once per request, at the very end none
page.controllers:after The page's controllers have run, just before render Page $page
page.render:before / page.render:after Around template rendering Page $page
page.create:after After Page::new() has saved a new page Page $page
page.delete:before Before a page is deleted, once for each descendant and then the page Page $page
page.delete:after After a page is deleted Page $page
page.update:after After Page::save() writes Page $page
page.move:after After a page is moved Page $old, Page $new
file.update:after After a page file is stored or its metadata updated PageFile $file
file.delete:after After a page file is deleted string $pageUuid, string $filename
image.save:after After an image variant is written string $variantPath
item.create:after After a collection item is created Item $item
item.update:after After a collection item is saved Item $item
item.delete:before Before a collection item is deleted Item $item
item.delete:after After a collection item is deleted Item $item
user.login:before / user.login:after Around a login attempt string $username / User $user
user.logout:before / user.logout:after Around logout ?User $user / none
user.create:after After a user is created User $user
user.update:after After a user is saved User $user
user.delete:before Before a user is deleted User $user

A create event fires once, when the new record has been saved, and carries the record as saved. Every later save fires update.

Example

Register listeners in an addon's index.php:

$app->on('app.route:after', function (Page $page) use ($app) {
    // read and observe
    error_log("Page resolved: {$page->title}");

    // don't mutate or redirect here: that breaks the observational contract
});