# Жизненный цикл запроса

Что происходит от момента, когда браузер постучался на сайт, до момента, когда отдаётся HTML.

## Шаг за шагом

1. **Веб-сервер.** `nginx` стоит спереди и отдаёт статику (css/js/img) напрямую. PHP-запросы проксируются в Apache/PHP-FPM, который по `.htaccess` направляет всё на `index.php` (фронт) или `admin.php` (админка).

2. **Точка входа.** `index.php` проверяет наличие `install.lock` (иначе — редирект на установку) и подключает `core/bootstrap.php`.

3. **Bootstrap + Kernel.** `core/bootstrap.php` создаёт `Kernel`, который:
   - подключает `Autoloader` (PSR-4);
   - строит DI-`Container`;
   - читает `Config` из `.env` и `config/*.php`;
   - регистрирует сервисы: `Connection` (БД, ленивый), `Engine` (шаблоны), `Router`, `Logger`, `Locale`, `Translator`, `Auth`, `Options` и др.;
   - запускает сессию (если не CLI).

4. **Локаль.** Определяется активный язык (`Locale::detect()`: `?lang` → cookie → `Accept-Language` → дефолт) и устанавливается в `Translator`.

5. **Маршруты.** `bootstrap.php` регистрирует публичные и админские маршруты. В конце подключаются активные плагины (`PluginManager::load()`) — их `plugin.php` добавляют свои маршруты, хуки и теги.

6. **Диспетчеризация.** `Kernel::handle($request)` вызывает `Router::dispatch()`. Роутер сопоставляет путь с шаблонами маршрутов и находит обработчик.

7. **Middleware.** Если у маршрута есть middleware (например `AuthMiddleware` для `/admin/*`), запрос проходит через них.

8. **Контроллер.** Роутер достаёт контроллер из контейнера (с авто-внедрением зависимостей) и вызывает метод `(Request $request, array $params)`. Контроллер возвращает `Response`.

9. **Ответ.** `Kernel::send($response)` выставляет статус, заголовки и отдаёт тело.

## Роутер

Маршрут регистрируется так:

```php
$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` создаёт объекты по рефлексии: смотрит типы аргументов конструктора и подставляет нужные сервисы. Поэтому контроллер просто объявляет зависимости:

```php
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

Объект ответа строится фабриками:

```php
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` на проде трейс не утечёт.
