Parameter Injection
PHP is a permissive language. ZealPHP is a permissive language that also reads — specifically, the signature of every handler you write.
You will learn
- The four magic parameter names ZealPHP injects for you
- How URL params (like {id}) get pulled into your function signature
- Why this is cached at registration — not done per request
- What happens when you ask for something ZealPHP doesn't know about
You declare. The framework fetches.
Most PHP frameworks pass you a request object and make you fish out what you need. Express
gives you (req, res, next). Laravel passes a Request instance you call
methods on. Symfony hands you the kitchen sink and lets you sort it out.
ZealPHP does something quieter: it reads your function signature with reflection, figures out what each parameter wants, and passes the right object by name. You write a handler that looks like a plain function. The framework treats the parameter list as a shopping list.
$app->route('/users/{id}', function ($id, $request, $response) {
return ['id' => $id, 'method' => $request->server['request_method']];
});
Three parameters, three different sources: $id from the URL pattern,
$request from the framework, $response from the framework. You
didn’t write a single line of plumbing.
The four magic names
ZealPHP recognizes four parameter-name patterns:
| If your parameter is named… | You get… |
|---|---|
$request | The ZealPHP\HTTP\Request wrapper — headers, query, body, cookies, files |
$response | The ZealPHP\HTTP\Response wrapper — json(), redirect(), stream(), sse(), cookie() |
$app | The ResponseMiddleware instance handling this request — almost never useful in modern handlers; reach for the static \ZealPHP\App::* facade (render(), include(), after(), getServer()) instead |
Anything matching a {name} in the route | The captured URL segment as a string |
Any other parameter with a default value gets its default. A parameter without a default that
ZealPHP can’t resolve raises a clear error at request time — not a silent null.
Examples by parameter name
1. Just the URL params
$app->route('/posts/{slug}', function ($slug) {
return Post::findBySlug($slug);
});
Cleanest possible handler. ZealPHP returns the array as JSON.
2. URL params + request body
$app->route('/posts/{slug}/comment', ['method' => 'POST'],
function ($slug, $request) {
$body = $request->post['comment'] ?? '';
Comment::create($slug, $body);
return ['ok' => true];
}
);
3. URL params + response (for cookies / streaming / redirects)
$app->route('/login/{token}', function ($token, $response) {
$response->cookie('session', $token, time() + 86400, '/', '', true, true);
return $response->redirect('/dashboard');
});
4. Mix everything
$app->route('/users/{id}/avatar', function ($id, $request, $response) {
$size = (int)($request->get['size'] ?? 64);
return $response->sendFile("storage/avatars/{$id}-{$size}.png");
});
Why this is fast
Reflection is famously slow in PHP. ZealPHP runs it once per route, at registration time, builds a parameter map (just an array of "this position gets the URL param named id, that one gets the request object"), and caches it on the route. At request time, the framework walks the cached map, not the reflection metadata.
The result: parameter injection costs you an array walk and a few function calls. The reflection cost is amortized across every request the worker handles — thousands of them — and in coroutine mode, the worker handles a lot.
Try it live
Every parameter-injection case is wired up in the demo app at /demo/inject/{case}.
Visit a few to see what gets injected:
- URL param only —
function ($id) - $request only —
function ($request) - URL param + $response — sets a custom header
- All three: $id + $request + $response
- Default parameter values —
function ($id, $page = 1)
You register $app->route("/posts/{slug}", function ($request, $slug) {...}). Does the order of parameters matter?
Key Takeaways
- Four magic names:
$request,$response,$app, and any URL{placeholder}. - Parameter order in your handler signature doesn't matter — injection is by name.
- Reflection runs once at route registration; the parameter map is cached — per-request injection is just an array walk.
- Any parameter with a default value gets its default if not injected.
- Parameters ZealPHP can't resolve raise an error — never silently get
null.