Жизненный цикл запроса
Жизненный цикл запроса
Что происходит от момента, когда браузер постучался на сайт, до момента, когда отдаётся HTML.
Шаг за шагом
- Веб-сервер.
nginxстоит спереди и отдаёт статику (css/js/img) напрямую. PHP-запросы проксируются в Apache/PHP-FPM, который по.htaccessнаправляет всё наindex.php(фронт) илиadmin.php(админка).
- Точка входа.
index.phpпроверяет наличиеinstall.lock(иначе — редирект на установку) и подключаетcore/bootstrap.php.
- Bootstrap + Kernel.
core/bootstrap.phpсоздаётKernel, который:- подключает
Autoloader(PSR-4); - строит DI-
Container; - читает
Configиз.envиconfig/*.php; - регистрирует сервисы:
Connection(БД, ленивый),Engine(шаблоны),Router,Logger,Locale,Translator,Auth,Optionsи др.; - запускает сессию (если не CLI).
- подключает
- Локаль. Определяется активный язык (
Locale::detect():?lang→ cookie →Accept-Language→ дефолт) и устанавливается вTranslator.
- Маршруты.
bootstrap.phpрегистрирует публичные и админские маршруты. В конце подключаются активные плагины (PluginManager::load()) — ихplugin.phpдобавляют свои маршруты, хуки и теги.
- Диспетчеризация.
Kernel::handle($request)вызываетRouter::dispatch(). Роутер сопоставляет путь с шаблонами маршрутов и находит обработчик.
- Middleware. Если у маршрута есть middleware (например
AuthMiddlewareдля/admin/*), запрос проходит через них.
- Контроллер. Роутер достаёт контроллер из контейнера (с авто-внедрением зависимостей) и вызывает метод
(Request $request, array $params). Контроллер возвращаетResponse.
- Ответ.
Kernel::send($response)выставляет статус, заголовки и отдаёт тело.
Роутер
Маршрут регистрируется так:
$router->get('/news/{slug:[^/]+}', NewsController::class . '@publicShow');
$router->post('/api/leads/submit', LeadsController::class . '@submit');
$router->group(['prefix' => '/admin', 'middleware' => [AuthMiddleware::class]], function (Router $r) {
$r->get('/users', UsersController::class . '@index');
});
{slug}— параметр (по умолчанию[^/]+), можно задать своё регулярное выражение:{id:\d+}.- Группы добавляют общий префикс и middleware.
- Обработчик
Class@methodленится: класс создаётся через контейнер только при совпадении маршрута.
Контейнер и авто-внедрение
Container создаёт объекты по рефлексии: смотрит типы аргументов конструктора и подставляет нужные сервисы. Поэтому контроллер просто объявляет зависимости:
final class MyController
{
public function __construct(
private readonly Engine $engine,
private readonly Options $options,
) {}
public function show(Request $request, array $params): Response
{
$this->engine->setTheme((string) $this->options->get('theme', 'default'));
return Response::html($this->engine->render('my-template', ['x' => 1]));
}
}
Регистрации: bind() — новый экземпляр на каждый запрос, singleton() — один на приложение, instance() — заранее готовый объект.
Response
Объект ответа строится фабриками:
Response::html($html); // text/html
Response::json($data); // application/json
Response::redirect('/url'); // 302
Response::notFound(); // 404
Response::text('ok'); // text/plain
Обработка ошибок
В production (APP_ENV=production) трассировка наружу не отдаётся — пользователь видит «500 Server Error», а детали пишутся в storage/logs. В dev-режиме показывается полный трейс. Это поведение принудительное: даже при случайно оставленном APP_DEBUG=true на проде трейс не утечёт.