<? phpukraine СПІВБЕСІДИ
Пошук по платформі
LARAVEL · JUNIOR ЧАСТО ПИТАЮТЬ

Як проходить запит через Laravel від index.php до відповіді?

public/index.php підключає автолоадер і bootstrap/app.php, зібраний застосунок віддає запит HTTP-ядру: воно виконує бутстрапери (env, config, фасади, провайдери), проганяє запит через глобальні middleware, роутер знаходить маршрут, запускає його middleware й контролер, а відповідь повертається тим самим ланцюгом назад до send() і terminate().

Розкажіть життєвий цикл запиту в Laravel 11: що відбувається після того, як nginx віддав керування PHP?
Де у Laravel 11 подівся app/Http/Kernel.php і куди тепер додавати middleware?
У якому місці циклу спрацьовує middleware з групи web — до чи після того, як роутер знайшов маршрут?
Хто перетворює масив, який повернув контролер, на JSON-відповідь?
lifecycle middleware bootstrap/app.php service providers

Точка входу — public/index.php. Він підключає автолоадер Composer і файл bootstrap/app.php, який повертає вже зібраний обʼєкт Illuminate\Foundation\Application, тобто service container застосунку. Далі викликається handleRequest(Request::capture()): Request::capture() будує обʼєкт запиту з суперглобальних масивів PHP, а handleRequest дістає з контейнера HTTP-ядро (Illuminate\Foundation\Http\Kernel) і виконує три кроки — handle($request), send() на отриманій відповіді та terminate(). Ядро першим ділом проганяє бутстрапери у фіксованому порядку: читання .env, завантаження конфігурації, встановлення обробників помилок, реєстрація фасадів, реєстрація провайдерів, boot() провайдерів. Саме тому в register() не можна нічого резолвити: на цьому кроці інші провайдери ще не зареєстровані.

У Laravel 11 і 12 усе налаштування зібрано в bootstrap/app.php через Application::configure(). Класів app/Http/Kernel.php і app/Console/Kernel.php у застосунку більше немає, як і масивів middleware та providers у config/app.php. Замість них — withRouting() (файли маршрутів, health-маршрут /up), withMiddleware() (глобальний стек, групи web і api, псевдоніми, пріоритети) та withExceptions() (звітування й рендеринг помилок). Провайдери застосунку перелічені окремо у bootstrap/providers.php, а провайдери пакетів фреймворк знаходить сам через package discovery і кешує у bootstrap/cache/packages.php. Група api існує лише після php artisan install:api — за замовчуванням у новому проєкті є тільки routes/web.php.

Коли застосунок піднятий, запит іде в Pipeline глобальних middleware. Вони бачать його ще до того, як хтось знає маршрут: тут місце для примусового HTTPS, request-id, обрізання пробілів. Потім dispatchToRouter передає запит роутеру, той шукає збіг за методом і URI, і вже для знайденого маршруту запускає його групові та маршрутні middleware — сесію, CSRF, auth, SubstituteBindings, який перетворює {post} в URL на модель. Тільки після цього контейнер створює контролер і резолвить залежності його методу за type hint. Повернене значення нормалізується роутером: рядок стає Response, масив або ArrayableJsonResponse, обʼєкт із Responsable віддає toResponse($request).

Далі відповідь рухається у зворотному напрямку — через ті самі middleware, але вже через код після $next($request). На цьому проході додаються заголовки й кукі, зберігається сесія. Це і є найкорисніша метафора для співбесіди: middleware — цибулина, а не список фільтрів. Будь-який шар може не викликати $next і повернути власну відповідь — так працюють редіректи гостя на форму входу й abort(403). Після цього send() віддає заголовки й тіло клієнту, а terminate() викликає terminate() у terminable middleware і відкладені через defer() колбеки. Під PHP-FPM користувач на цей момент відповідь уже отримав, але процес усе ще зайнятий: черга це не замінює.

Межі такої моделі варто знати навіть джуну. Увесь описаний цикл — це один запит на один процес: після terminate() PHP знищує обʼєкти й контейнер, тож стан між запитами не тече. Саме тому статичні змінні та властивості синглтонів здаються безпечними — під звичайним FPM вони живуть мілісекунди. Під Laravel Octane застосунок піднімається один раз і залишається в памʼяті, бутстрап не повторюється, і ті самі синглтони починають зберігати чужі дані. Кеш конфігурації (config:cache) і маршрутів (route:cache) також скорочують крок бутстрапу, але роблять env() поза config/ порожнім — це наслідок саме того, що LoadEnvironmentVariables при закешованому конфізі не робить нічого корисного для коду застосунку.

// public/index.php у Laravel 11/12 — по суті два рядки роботи
require __DIR__.'/../vendor/autoload.php';
(require_once __DIR__.'/../bootstrap/app.php')->handleRequest(Request::capture());
// handleRequest бере HTTP-ядро з контейнера, викликає handle(), send() і terminate()

// bootstrap/app.php — єдине місце налаштування замість двох Kernel-класів
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',      // маршрути з групою web
        commands: __DIR__.'/../routes/console.php',
        health: '/up',                          // готовий health-check маршрут
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->append(EnsureRequestId::class);   // глобальний: до роутера
        $middleware->web(append: [SetLocale::class]);  // лише для групи web
        $middleware->alias(['subscriber' => EnsureUserIsSubscribed::class]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->dontReport(PaymentDeclinedException::class);
    })->create();

// Middleware — «цибулина»: до $next іде запит, після $next — уже відповідь
final class SetLocale
{
    public function handle(Request $request, Closure $next): Response
    {
        App::setLocale($request->user()?->locale ?? 'uk'); // ще до контролера

        if ($request->hasHeader('X-Blocked')) {
            return response('Заборонено', 403); // ланцюг обірвано, контролера не буде
        }

        $response = $next($request); // тут уже відпрацювали роутер і контролер

        return $response->header('Content-Language', App::getLocale()); // зворотний прохід
    }
}
Послідовність без пропусків: index.php → autoload → bootstrap/app.php → HTTP-ядро → бутстрапери → глобальні middleware → роутер → middleware маршруту → контролер → відповідь назад через middleware → send() → terminate().
Що в Laravel 11 і 12 bootstrap/app.php замінив app/Http/Kernel.php, app/Console/Kernel.php і масиви middleware та providers з config/app.php.
Що провайдери підключаються у два проходи: спершу register() у всіх, потім boot() у всіх; список застосунку — у bootstrap/providers.php, пакетні знаходяться через package discovery.
Що middleware двонаправлений: код до $next($request) бачить запит, код після — уже готову відповідь, і будь-який middleware може обірвати ланцюг, не викликавши $next.
Що контролер не зобовʼязаний повертати об'єкт Response: рядок, масив, Arrayable чи Responsable роутер сам приводить до Response або JsonResponse.
Казати, що index.php підключає app/Http/Kernel.php: у Laravel 11+ цього класу в застосунку немає, ядро живе у фреймворку, а налаштування — у bootstrap/app.php.
Вважати, що middleware з групи web виконується до роботи роутера: групові й маршрутні middleware запускаються вже після того, як маршрут знайдено, до них ідуть тільки глобальні.
Плутати register() і boot(): резолвити в register() сервіси або читати конфіг, який ще міг не завантажитись іншим провайдером.
Думати, що відповідь формує лише контролер, і не згадувати зворотний прохід, де middleware додає заголовки, кукі й зберігає сесію.
Плутати terminate() з чергою: він виконується у тому ж процесі після відправки відповіді, без ретраїв, і важку роботу туди класти не можна.
ПОРАДА

Опишіть цикл як цибулину: запит іде всередину крізь шари middleware до контролера, а відповідь повертається тими самими шарами назовні. Окремо назвіть bootstrap/app.php як єдину точку налаштування в Laravel 11+ і два проходи провайдерів — register(), потім boot().

оновлено 4 вересня 2026 · ліцензія CC-BY-SA-4.0 Знайшли неточність? Напишіть →
ПЕРЕВІРТЕ СЕБЕ

Глобальні middleware бачать запит до роутера, групові й маршрутні — після пошуку маршруту, а відповідь проходить ті самі шари у зворотному напрямку.