Request Lifecycle
This page explains what happens between an HTTP request and the response a user receives.
Lifecycle Overview
Browser or API client
Sends the HTTP request.
Front controller
public/index.php loads the app and starts dispatch.
Bootstrap
Framework services, environment values, and request handling are prepared.
Routes
public/routes.php and app route files register URL handlers.
Middleware
Request gates can allow, reject, or wrap the handler.
Controller
The matched action coordinates the request.
Module or repository
App-owned logic loads data, validates input, or writes persistence.
View or response
The app renders HTML or returns a PSR-7 response.
Understanding this flow helps you decide where code belongs.
1. Front Controller
Requests enter through:
public/index.php
The front controller loads environment configuration, creates the container, registers framework services, starts request handling, and dispatches the router.
You normally do not edit this file for features.
2. Route Registration
Application routes start from:
public/routes.php
Small projects can keep routes there. Larger projects should split them:
$blogRoutes = PROJECT_ROOT . '/src/Routes/blog.php';
if (is_file($blogRoutes)) {
(require $blogRoutes)($router);
}
Generate a route file with:
php coriander make:route blog
3. Middleware
Middleware runs before or around the route handler. Use it for request gates:
- authentication
- admin-only sections
- API limits
- security headers
- CSRF protection
Example group:
use Middleware\AdminMiddleware;
$router->group('admin', [new AdminMiddleware()], function ($router): void {
$router->get('dashboard', fn () => 'Admin dashboard');
});
Middleware should not render complex pages or perform business writes. It should decide whether the request may continue.
4. Controller
Controllers coordinate the request:
final class BlogController
{
public function index(): void
{
$posts = (new BlogRepository())->latest();
$this->view->render('blog/index', [
'posts' => $posts,
]);
}
}
Keep controllers readable. If a method becomes a long business workflow, move the workflow into a service under src/Modules.
5. Module Or Repository
Modules hold app-owned logic. Repositories hold persistence details.
namespace Modules\Blog;
use CorianderCore\Core\Database\SQLManager;
final class BlogRepository
{
public function latest(): array
{
$rows = SQLManager::sqlScript(
'SELECT id, title FROM posts ORDER BY created_at DESC LIMIT 10'
);
return $rows === [] ? [] : (array_is_list($rows) ? $rows : [$rows]);
}
}
This keeps SQL out of views and keeps controllers focused on flow.
6. View Or Response
HTML pages render views from:
public/public_views/
API endpoints return JSON responses instead of HTML views.
Use views for presentation, not data loading. Escape public strings before output.
Write Request Flow
For forms and other writes, use Post/Redirect/Get:
POST route
Receives submitted form data.
Validation
Checks required fields and input shape.
Authorization
Confirms the current user may perform the action.
Write service
Writes through a service or repository.
Flash
Stores a one-time result message.
Redirect
Returns a 302 to the GET page.
This prevents browser refresh from resubmitting the form.
Debugging The Flow
When a route does not work, check in order:
Route file
Is the route file required by public/routes.php?
URL pattern
Does the pattern match the actual request?
Middleware
Is middleware blocking the request?
Controller
Is the controller method being called?
View
Does the controller render an existing view?
Module or repository
Does app-owned logic throw an exception?