Application Lifecycle
How requests flow through the framework.
Understanding the request lifecycle helps you know where to hook in custom logic and debug issues.
Entry Points #
| Entry | File | Purpose |
|---|---|---|
| HTTP | public/index.php |
Web requests |
| CLI | velvet |
Console commands |
| Tests | tests/bootstrap.php |
PHPUnit bootstrap |
HTTP Request Flow #
Request → Tenancy → Bootstrap → Providers → Assets → Routes → Middleware → Controller → Response
1. Capture Request #
The request arrives at public/index.php. A Request object is created from PHP globals.
2. Resolve Tenancy #
If tenancy is enabled, the tenant is resolved from the host, path, or custom resolver. This sets up tenant-scoped paths for the rest of the request.
3. Bootstrap Application #
bootstrap/app.php creates the Application container and registers core bindings.
4. Boot Providers #
CoreServiceProvider registers framework services. Then module service providers are registered and booted in dependency order.
Events fired:
app.booting- before providers bootapp.booted- after all providers boot
5. Serve Assets #
The AssetServer checks if the request matches /assets/*. If so, it serves the static file and exits early.
6. Register Routes #
If routes aren't cached, route files are loaded. Module routes are registered via their service providers.
7. Dispatch Through Middleware #
The request passes through global middleware, then route-specific middleware.
Events fired:
router.matching- when matching startsrouter.matched- when a route matches
8. Execute Controller #
The matched route's handler runs. Controllers are resolved from the container with dependencies injected.
9. Return Response #
The controller returns a Response (or a value that's converted to one). The response is sent to the client.
CLI Flow #
Command → Tenancy → Bootstrap → Register Commands → Execute
1. Parse Command #
The velvet script parses the command name and arguments.
2. Resolve Tenancy #
If TENANCY_TENANT is set, that tenant context is used. Otherwise, the default tenant applies.
3. Bootstrap Application #
The app bootstraps if possible. Some commands (like install) run without full bootstrap.
4. Register Commands #
Built-in commands register first. Then the commands.registering event fires, allowing modules to add their commands.
5. Execute Command #
The command runs and returns an exit code.
Key Hooks #
Where to add your logic:
| Need | Where |
|---|---|
| Early initialization | app.booting event |
| After app ready | app.booted event |
| Before route matching | router.matching event |
| After route matched | router.matched event |
| Transform request | Global middleware |
| Auth/validation | Route middleware |
| Business logic | Controllers/Services |