Request Lifecycle
This page explains what happens between an HTTP request and the response a user receives.
Lifecycle Overview
Browser or API client
-> public/index.php
-> framework bootstrap
-> public/routes.php
-> route file
-> middleware
-> controller
-> module or repository
-> view or 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 /posts
-> validate input
-> authorize action
-> write through service or repository
-> store flash message
-> redirect to GET page
This prevents browser refresh from resubmitting the form.
Debugging The Flow
When a route does not work, check in order:
- Is the route file required by
public/routes.php? - Does the URL pattern match the actual request?
- Is middleware blocking the request?
- Is the controller method being called?
- Does the controller render an existing view?
- Does the module/repository throw an exception?