Життєвий цикл запиту
HTTP-запит проходить від веб-сервера через public/index.php, реєстрацію service providers,
резолв Router і PSR-7 Request, диспетчеризацію League Route і відправку відповіді
через SapiEmitter.
Передумови: налаштування точок входу, конфігурація.
Огляд
flowchart TB
WS["Веб-сервер"]
IDX["public/index.php"]
BOOT["bootstrap/app.php"]
PROV["registerServiceProviders()"]
RUN["App::run()"]
ROUTER["Router::dispatch(Request)"]
MW["Middleware stack"]
STRAT["RouteStrategy"]
INT["Route interceptors"]
RES["Autowire: FormRequest, params, Request"]
CTRL["Controller / closure"]
EMIT["SapiEmitter"]
WS --> IDX --> BOOT --> PROV
IDX --> RUN --> ROUTER --> MW --> STRAT
STRAT --> INT --> RES --> CTRL --> EMIT
Веб-сервер → public/index.php
→ vendor/autoload.php
→ bootstrap/app.php
→ App::create() + registerServiceProviders()
→ для кожного провайдера: register(), потім boot()
→ ComponentsServiceProvider::boot() підключає routes компонентів
→ App::run()
→ container->get(RouterInterface)
→ container->get(ServerRequestInterface)
→ Router::dispatch(Request)
→ middleware (глобальний + маршруту)
→ RouteStrategy::invokeRouteCallable()
→ route interceptors
→ autowire аргументів handler
→ PSR-7 Response
→ SapiEmitter → клієнт
1. Точка входу
public/index.php — єдиний файл, на який має дивитися document root.
Він не містить бізнес-логіки:
require_once __DIR__ . '/../vendor/autoload.php';
/** @var \Concept\Core\App $app */
$app = require_once __DIR__ . '/../bootstrap/app.php';
$app->run();
2. Bootstrap (до run)
bootstrap/app.php виконує:
App::create($rootPath, $paths)— League Container,ReflectionContainer,PathManager, ранній Whoops (читаєAPP_DEBUGз$_ENV).registerServiceProviders()— підключення списку зbootstrap/providers/app.php.
League Container для кожного провайдера викликає register(), потім одразу
boot() (якщо реалізовано BootableServiceProviderInterface) — див.
Провайдери → register і boot.
Порядок у providers/app.php критичний: провайдер N у boot() може
використовувати сервіси лише від провайдерів вище у списку.
Порядок провайдерів (HTTP, skeleton)
Фактичний порядок у bootstrap/providers/app.php:
- ConfigServiceProvider — конфіг,
.env, часовий пояс - LocaleServiceProvider — локаль для перекладів валідації
- TelemetryServiceProvider — збір телеметрії (якщо увімкнено)
- ErrorHandlerServiceProvider — Whoops (HTML / JSON / production fallback)
- HttpServiceProvider — PSR-7 запит, Router, фабрики відповідей
- SessionServiceProvider — Symfony Session (стартує при першому зверненні)
- DataMaskerServiceProvider — маскування чутливих даних у логах
- LogServiceProvider — Monolog
- ViewRegistryServiceProvider, TwigServiceProvider
- ValidationServiceProvider, DatabaseServiceProvider, CastingServiceProvider
- DebugLoggerServiceProvider — in-memory debug log (dev)
- ComponentsServiceProvider — модулі (останній у списку)
Що робить ComponentsServiceProvider::boot()
ComponentsServiceProvider стоїть останнім, щоб у boot() уже були
ConfigInterface, RouterInterface, view registries тощо.
Під час boot() (ще до App::run()), якщо увімкнені компоненти:
- реєструються CLI-команди, міграції та сидери компонентів;
- підключаються додаткові service providers компонентів (
addServiceProvider()); requireфайлівroutes.phpкожного компонента — маршрути додаються в той самийRouter;- реєструються Twig paths, extensions, contexts для views компонентів (лише HTTP, не CLI).
Глобальні маршрути з routes/web.php підключаються при першому резолві
RouterInterface у фабриці HttpServiceProvider — зазвичай під час
ComponentsServiceProvider::boot(), коли він запитує Router для component routes.
3. PSR-7 запит
ServerRequestInterface створюється в контейнері через
Laminas\Diactoros\ServerRequestFactory::fromGlobals() —
дані з $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES.
У типовому HTTP-циклі резолв відбувається в App::run() перед dispatch().
Після збігу маршруту RouteStrategy додає параметри URL у атрибути запиту
({id} → $request->getAttribute('id')) і оновлює shared-екземпляр
у контейнері.
4. App::run()
/** @var RouterInterface $router */
$router = $this->container->get(RouterInterface::class);
/** @var ServerRequestInterface $request */
$request = $this->container->get(ServerRequestInterface::class);
$response = $router->dispatch($request);
(new SapiEmitter)->emit($response);
Router (клас Concept\Core\Http\Routing\Router) — обгортка над League Route
з RouteStrategy: DI параметрів handler, FormRequest, route interceptors, телеметрія.
5. Middleware
Middleware реєструється в файлах маршрутів через
$router->lazyMiddleware(ClassName::class) (League Route).
Класи створюються з контейнера під час обробки запиту.
Кожен middleware реалізує MiddlewareInterface::process($request, $handler)
і викликає $handler->handle($request) для передачі далі по ланцюгу.
У ядрі є, зокрема:
HandleValidationExceptionMiddleware— перетворюєValidationExceptionна відповідьVerifyCsrfTokenMiddleware— CSRF для небезпечних методівStorePreviousUrlMiddleware— зберігає URL для редіректівShareViewDataMiddleware— спільні змінні для Twig (csrf_token тощо)ParseJsonBodyMiddleware,ForceJsonResponseMiddleware— JSON API
Мінімальний skeleton не підключає middleware глобально — лише маршрут /.
Детальніше — Middleware.
6. RouteStrategy: interceptors і handler
Після middleware League Route викликає RouteStrategy::invokeRouteCallable():
- Route interceptors — класи з
config/routes.php→routes.interceptors(RouteInterceptorInterface::intercept()); для ACL, політик тощо — див. Route interceptors. - Параметри URL у Request —
{id}стає атрибутом запиту. - Autowire аргументів handler — для методу контролера або closure:
FormRequest— з контейнера, валідація; при помилці —ValidationExceptionServerRequestInterface— поточний запит- параметри маршруту — з приведенням типів через
CasterInterface(Valinor) - значення за замовчуванням у сигнатурі
Приклад маршруту:
$router->get('/', [IndexController::class, 'index'])->setName('home');
Контролер [Class, 'method'] резолвиться з контейнера — залежності конструктора
(ViewResponseFactoryInterface тощо) інжектуються через League Container +
ReflectionContainer.
Детальніше — Контролери → автовайринг,
аргументи методу.
Якщо telemetry.enabled, RouteStrategy фіксує
TelemetryEvent::HTTP_ROUTE_INTERCEPTOR_EXECUTED (кожен interceptor) і
TelemetryEvent::HTTP_ROUTE_CALLABLE_INVOKED (час виконання handler).
7. Відповідь
Handler повертає ResponseInterface (PSR-7). Типові варіанти:
ViewResponseFactoryInterface::create('@frontend/index')— HTML через TwigResponseFactoryInterface— JSON, redirect, порожня відповідь
SapiEmitter (Laminas HttpHandlerRunner) відправляє заголовки та тіло в PHP SAPI
(Apache, FPM, built-in server). Детальніше — Відповіді.
8. Обробка помилок
- До провайдерів — ранній Whoops у
App::create()(читаєAPP_DEBUGз$_ENV). - ErrorHandlerServiceProvider::boot() — перемикає Whoops: PrettyPage (debug), PlainText (CLI), JSON (
RequestFormat::expectsJson()), production fallback зPathName::ERRORS_FALLBACK_VIEWS. - Валідація —
ValidationExceptionз FormRequest;HandleValidationExceptionMiddlewareможе повернути 422 / redirect. - CSRF —
CsrfExceptionпри невалідному токені. - 404 / метод — League Route; відповідь залежить від налаштувань router і middleware.
9. Телеметрія (опційно)
Якщо telemetry.enabled = true, ядро записує етапи bootstrap і HTTP через
TelemetryCollector: пробудження сервісів (FRAMEWORK_SERVICE_AWAKENING),
реєстрацію компонентів, завантаження route-файлів (FRAMEWORK_ROUTES_REGISTERED),
span interceptor і handler, підсумок запиту (HTTP_REQUEST_HANDLED у App::run()).
Опційно: SQL (telemetry.db_queries) і дзеркало логів (telemetry.logs).
CLI — окремий цикл
bin/console.php не викликає App::run().
Використовує той самий paths.php і App::create(), але провайдери з
bootstrap/providers/console.php і Symfony Console Application::run().
HTTP middleware, PSR-7 запит і Router::dispatch() у CLI не застосовуються.
Далі
- Маршрутизація — оголошення маршрутів, імена, групи
- Route interceptors — ACL, винятки, редирект через middleware
- Middleware — власний middleware
- Контролери
- Запити та валідація — FormRequest