Жизненный цикл запроса в Laravel
Обзор
Понимание жизненного цикла HTTP-запроса -- это фундаментальное знание для Senior Laravel-разработчика. Каждый запрос проходит через строго определённую последовательность этапов.
Высокоуровневая схема
Браузер → Веб-сервер (Nginx/Apache) → public/index.php →
→ Bootstrap Application → Load Service Providers →
→ Route Matching → Middleware Pipeline →
→ Controller/Action → Response →
→ Terminable Middleware → Terminate
Этап 1: Точка входа (public/index.php)
<?php
declare(strict_types=1);
// public/index.php — the SINGLE entry point for all HTTP requests
// 1. Record the start time for performance measurement
define('LARAVEL_START', microtime(true));
// 2. Check if application is in maintenance mode
// This happens BEFORE framework bootstrap for performance
if (file_exists($maintenance = __DIR__ . '/../storage/framework/maintenance.php')) {
require $maintenance;
// If in maintenance mode, a pre-rendered response is sent
// and the script exits here (framework never boots)
}
// 3. Register Composer autoloader
// This enables PSR-4 class autoloading for all packages
require __DIR__ . '/../vendor/autoload.php';
// 4. Bootstrap the application
// Creates Application instance and configures it
/** @var \Illuminate\Foundation\Application $app */
$app = require_once __DIR__ . '/../bootstrap/app.php';
// 5. Handle the incoming request
$app->handleRequest(
\Illuminate\Http\Request::capture()
);
Для экзамена: Проверка режима обслуживания (maintenance mode) выполняется ДО загрузки фреймворка. Это означает, что приложение может отвечать на запросы в режиме обслуживания без полной инициализации, что обеспечивает минимальное потребление ресурсов.
Этап 2: Создание приложения (bootstrap/app.php)
<?php
declare(strict_types=1);
// bootstrap/app.php — Application configuration (Laravel 11)
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__ . '/../routes/web.php',
api: __DIR__ . '/../routes/api.php',
commands: __DIR__ . '/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
// Configure middleware stack
})
->withExceptions(function (Exceptions $exceptions) {
// Configure exception handling
})
->create();
Что происходит внутри Application::configure()
<?php
declare(strict_types=1);
// Internally, Application::configure() does:
// 1. Creates new Application instance
$app = new \Illuminate\Foundation\Application($basePath);
// 2. Sets base path and derived paths
// app_path(), config_path(), database_path(), etc.
// 3. Registers core bindings in the container:
$app->singleton(
\Illuminate\Contracts\Http\Kernel::class,
\Illuminate\Foundation\Http\Kernel::class,
);
$app->singleton(
\Illuminate\Contracts\Console\Kernel::class,
\Illuminate\Foundation\Console\Kernel::class,
);
$app->singleton(
\Illuminate\Contracts\Debug\ExceptionHandler::class,
\Illuminate\Foundation\Exceptions\Handler::class,
);
// 4. The Application class IS the Service Container
// Application extends Container implements ApplicationContract
Ключевой факт: Класс
ApplicationрасширяетContainerи реализует интерфейсApplicationContract. Это означает, что экземпляр приложения является сервис-контейнером. Когда вы вызываетеapp(), вы получаете и приложение, и контейнер одновременно.
Этап 3: Захват HTTP-запроса
<?php
declare(strict_types=1);
// Request::capture() creates an Illuminate Request from PHP superglobals
use Illuminate\Http\Request;
// Internally, this does:
$request = Request::capture();
// Which calls:
// Request::createFromBase(SymfonyRequest::createFromGlobals())
// The request object encapsulates:
// - $_GET → $request->query
// - $_POST → $request->request
// - $_FILES → $request->files
// - $_COOKIE → $request->cookies
// - $_SERVER → $request->server
// - Headers from $_SERVER
// - Raw body (php://input)
// Laravel's Request extends Symfony's Request
// Adds convenient methods like:
$request->input('key'); // Get input from any source
$request->query('key'); // Get from query string
$request->post('key'); // Get from POST data
$request->file('photo'); // Get uploaded file
$request->header('Accept'); // Get header value
$request->bearerToken(); // Get Bearer token
$request->ip(); // Client IP address
$request->method(); // HTTP method (GET, POST, etc.)
$request->path(); // Request path without query string
$request->url(); // Full URL without query string
$request->fullUrl(); // Full URL with query string
Этап 4: Обработка запроса через ядро (Kernel)
<?php
declare(strict_types=1);
// The HTTP Kernel handles the request through a pipeline
// In Laravel 11, the Kernel is internal (no app/Http/Kernel.php)
// But conceptually, handleRequest() does:
// 1. Bootstrap the application (if not already bootstrapped)
$this->bootstrap();
// Bootstrapping includes these bootstrappers IN ORDER:
$bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
Порядок выполнения Bootstrappers
<?php
declare(strict_types=1);
// 1. LoadEnvironmentVariables
// - Loads .env file using vlucas/phpdotenv
// - SKIPPED if config is cached (bootstrap/cache/config.php exists)
// - Sets values in $_ENV and $_SERVER superglobals
// 2. LoadConfiguration
// - If cached: loads bootstrap/cache/config.php
// - If not cached: loads all files from config/ directory
// - Makes config available through config() helper
// 3. HandleExceptions
// - Sets error reporting level
// - Registers error handler (converts errors to exceptions)
// - Registers exception handler
// - Registers shutdown handler
// 4. RegisterFacades
// - Registers the Facade class alias loader
// - Loads package-discovered facades
// - Enables using facades without imports in some contexts
// 5. RegisterProviders
// - Loads providers from bootstrap/providers.php
// - Loads package-discovered providers (bootstrap/cache/packages.php)
// - Calls register() method on each provider
// 6. BootProviders
// - Calls boot() method on each registered provider
// - This is where providers set up event listeners, routes, view composers, etc.
Ловушка экзамена: Порядок bootstrappers критически важен. Сначала загружаются переменные окружения, затем конфигурация, затем обработка ошибок. Сервис-провайдеры регистрируются ПОСЛЕ загрузки конфигурации, поэтому в
register()методе провайдера можно использоватьconfig(), но нельзя полагаться на другие провайдеры.
Этап 5: Middleware Pipeline
<?php
declare(strict_types=1);
// After bootstrapping, the request goes through middleware pipeline
// The pipeline pattern:
// Request → Middleware1 → Middleware2 → ... → Controller → Response
// Response ← Middleware1 ← Middleware2 ← ... ←
// Global middleware (applied to ALL requests):
// In Laravel 11, these are built into the framework:
// - TrustProxies
// - HandleCors
// - PreventRequestsDuringMaintenance
// - ValidatePostSize
// - TrimStrings
// - ConvertEmptyStringsToNull
// Web middleware group (routes/web.php):
// - EncryptCookies
// - AddQueuedCookiesToResponse
// - StartSession
// - ShareErrorsFromSession
// - VerifyCsrfToken
// - SubstituteBindings
// API middleware group (routes/api.php):
// - SubstituteBindings
// - (Sanctum's EnsureFrontendRequestsAreStateful if configured)
Как работает Pipeline
<?php
declare(strict_types=1);
namespace Illuminate\Pipeline;
use Closure;
// Simplified view of Laravel's Pipeline
final class Pipeline
{
private array $pipes = [];
private mixed $passable;
public function send(mixed $passable): static
{
$this->passable = $passable;
return $this;
}
public function through(array $pipes): static
{
$this->pipes = $pipes;
return $this;
}
public function then(Closure $destination): mixed
{
// Build a nested closure chain
// Each middleware wraps the next one
$pipeline = array_reduce(
array_reverse($this->pipes),
$this->carry(),
$destination
);
return $pipeline($this->passable);
}
private function carry(): Closure
{
return function (Closure $stack, string $pipe): Closure {
return function (mixed $passable) use ($stack, $pipe): mixed {
// Resolve middleware from container and call handle()
$instance = app($pipe);
return $instance->handle($passable, $stack);
};
};
}
}
// Usage in Kernel:
// (new Pipeline($this->app))
// ->send($request)
// ->through($middleware)
// ->then(fn ($request) => $router->dispatch($request));
Для Senior: Паттерн Pipeline -- это реализация Chain of Responsibility. Каждый middleware получает запрос и замыкание
$next. Middleware может: 1) обработать запрос ДО передачи дальше (before middleware), 2) обработать ответ ПОСЛЕ получения (after middleware), 3) отклонить запрос и вернуть собственный ответ.
Этап 6: Маршрутизация (Router)
<?php
declare(strict_types=1);
// After passing through global middleware,
// the Router matches the request to a route
// Route matching considers:
// 1. HTTP method (GET, POST, PUT, PATCH, DELETE)
// 2. URI pattern (with parameters)
// 3. Domain/subdomain constraints
// 4. Where conditions (regex constraints)
// When a match is found:
// 1. Route-specific middleware are resolved
// 2. Request goes through route middleware pipeline
// 3. Route action is executed (controller method or closure)
use Illuminate\Support\Facades\Route;
// The router resolves the controller from the container
// enabling automatic dependency injection
Route::get('/users/{user}', [UserController::class, 'show']);
// Resolution process:
// 1. Router finds matching route: /users/{user}
// 2. Route model binding resolves {user} to User model
// 3. Container resolves UserController with dependencies
// 4. Calls show() method with injected User instance
Этап 7: Контроллер / Action
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
final class UserController extends Controller
{
// Dependencies are injected via constructor
public function __construct(
private readonly UserService $userService,
) {}
// Method dependencies are also injected
public function show(Request $request, User $user): JsonResponse
{
// $user is already resolved via Route Model Binding
// $request is injected by the container
// Business logic executes here
$userData = $this->userService->getUserProfile($user);
// Return response
return response()->json($userData);
}
}
Этап 8: Формирование ответа
<?php
declare(strict_types=1);
// The controller returns a response
// Laravel accepts multiple return types:
// 1. String → converted to Response with text/html content type
return 'Hello World';
// 2. Array → converted to JSON response
return ['name' => 'John', 'age' => 30];
// 3. Eloquent Model/Collection → converted to JSON
return User::find(1);
// 4. Response object
return response('Content', 200)->header('Content-Type', 'text/plain');
// 5. JSON Response
return response()->json(['status' => 'ok']);
// 6. View
return view('users.show', ['user' => $user]);
// 7. Redirect
return redirect()->route('users.index');
// 8. Responsable interface
return new UserResource($user); // Implements Responsable
// The response passes BACK through middleware in REVERSE order
// This is the "after" phase of middleware
Responsable интерфейс
<?php
declare(strict_types=1);
namespace App\Http\Responses;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
final readonly class ApiResponse implements Responsable
{
public function __construct(
private mixed $data,
private int $status = 200,
private string $message = 'Success',
) {}
public function toResponse($request): JsonResponse
{
return response()->json([
'success' => $this->status < 400,
'message' => $this->message,
'data' => $this->data,
], $this->status);
}
}
// Usage in controller:
// return new ApiResponse(data: $user, message: 'User found');
Этап 9: Отправка ответа и завершение
<?php
declare(strict_types=1);
// After the response is formed:
// 1. Response headers and content are sent to the client
$response->send();
// Internally calls:
// - sendHeaders() — sends HTTP headers
// - sendContent() — sends response body
// 2. Terminable middleware are executed
// This happens AFTER the response is sent to the client
$kernel->terminate($request, $response);
// Terminable middleware example:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
final class LogRequestDuration
{
public function handle(Request $request, Closure $next): Response
{
$request->attributes->set('start_time', microtime(true));
return $next($request);
}
// Called AFTER response is sent to client
public function terminate(Request $request, Response $response): void
{
$duration = microtime(true) - $request->attributes->get('start_time');
// Log request duration without affecting response time
logger()->info('Request completed', [
'url' => $request->fullUrl(),
'method' => $request->method(),
'status' => $response->getStatusCode(),
'duration_ms' => round($duration * 1000, 2),
]);
}
}
Ключевое различие: Terminable middleware выполняется после отправки ответа клиенту. Это идеально для тяжёлых операций (логирование, статистика, очистка), которые не должны влиять на время ответа. Метод
terminate()вызывается только если middleware реализует его.
Полная последовательность событий
<?php
declare(strict_types=1);
// Complete request lifecycle — step by step:
// 1. Web server receives HTTP request
// 2. Request forwarded to public/index.php
// 3. Maintenance mode check (before framework boot)
// 4. Composer autoloader registered
// 5. Application instance created (bootstrap/app.php)
// 6. Bootstrappers execute in order:
// a. LoadEnvironmentVariables (.env parsing)
// b. LoadConfiguration (config/*.php loading)
// c. HandleExceptions (error/exception handlers)
// d. RegisterFacades (facade alias loader)
// e. RegisterProviders (register() on all providers)
// f. BootProviders (boot() on all providers)
// 7. Request captured from PHP superglobals
// 8. Global middleware pipeline (before):
// TrustProxies → HandleCors → PreventRequestsDuringMaintenance →
// ValidatePostSize → TrimStrings → ConvertEmptyStringsToNull
// 9. Route matching
// 10. Group middleware pipeline (web or api group)
// 11. Route-specific middleware
// 12. Route model binding resolution
// 13. Controller dependency injection & method call
// 14. Response formation
// 15. Response passes back through middleware (after, reverse order)
// 16. Response sent to client (headers + body)
// 17. Terminable middleware executed (after response sent)
// 18. Application terminated
Визуализация через дебаг
<?php
declare(strict_types=1);
// You can trace the lifecycle using Laravel Telescope
// or by adding debug logging to key points:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
final class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
// This runs during step 6e (RegisterProviders)
// Other providers may NOT be booted yet
// Only bind services, don't use other services
}
public function boot(): void
{
// This runs during step 6f (BootProviders)
// All providers are registered
// Safe to use any service
}
}