# Життєвий цикл запиту

Що відбувається від моменту, коли браузер постукав на сайт, до моменту, коли віддається 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` на проді трейс не витече.
