Event Hooks
A lightweight observational event system for reacting to app lifecycle stages without coupling to framework internals.
Overview
The App exposes on() and emit() for registering and firing lifecycle events. Listeners are observational only — they receive typed arguments but return values are ignored. Mutations belong at explicit layer boundaries (routes, controllers, Res::*), not inside event listeners.
This design aligns with PhlatPage's immutable data philosophy: the framework controls when state changes; hooks let you observe those changes.
API
// 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);
Naming convention
Event names follow a structured pattern:
domain.action:timing
domain.action(qualifier):timing
| Segment | Examples |
|---|---|
domain |
app, page, file, user — required |
action |
create, update, delete, render, login, route |
qualifier |
(slug), (title), (password) — optional, narrows scope |
timing |
:before, :after |
Every event name must begin with a domain. Bare action names like route:after are not valid — use app.route:after.
Qualified events are distinct — a listener on page.update(title):after will not fire when page.update(slug):after is emitted. The full event name must match exactly.
Current hooks
| Event | When | Args |
|---|---|---|
app.route:after |
After $page->route() resolves the routed page |
Page $page |
Addons
Event hooks are the primary way addons observe the request lifecycle without requiring changes to framework code. Register listeners in your addon's index.php:
$app->on('app.route:after', function(Page $page) use ($app) {
// populate a Clockwork panel, log to a file, etc.
});
Listeners run in registration order. Because addons load before routing, app.route:after is the earliest point at which the resolved Page is available.
<?php
use Phlat\App;
use Phlat\Page;
// In an addon's index.php — $app is injected
// Observe the resolved page after routing
$app->on('app.route:after', function(Page $page) use ($app) {
// Correct: read and observe
$title = $page->title;
error_log("Page resolved: {$title}");
// Wrong: do not mutate or redirect from a listener
// Res::redirect('/somewhere'); // violates the observational contract
});
// Multiple listeners on the same event are all called
$app->on('app.route:after', function(Page $page) {
// also fires
});