Laravel самый популярный PHP-фреймворк в российских продуктовых командах. На собесе спрашивают жизненный цикл запроса, сервис-контейнер, фасады, валидацию и очереди.
Жизненный цикл запроса
public/index.phpподключает автозагрузчик и создаёт приложение.- Ядро загружает окружение, конфигурацию, обработку ошибок.
- Сервис-провайдеры: сначала у всех
register(), потом у всехboot(). - Глобальные middleware, маршрутизатор находит маршрут, middleware маршрута.
- Контроллер с внедрёнными зависимостями возвращает ответ.
- Ответ проходит middleware обратно, затем terminable middleware.
В Laravel 11+ middleware, маршруты и обработка исключений настраиваются в bootstrap/app.php.
Сервис-контейнер
// AppServiceProvider::register()
$this->app->bind(PaymentGateway::class, YooKassaGateway::class); // новый объект каждый раз
$this->app->singleton(TaxCalculator::class, fn() => new TaxCalculator(config('tax.rate')));
$this->app->scoped(CurrentTenant::class); // сбрасывается между запросами
$this->app->when(InvoiceMailer::class)
->needs(Mailer::class)
->give(SmtpMailer::class); // контекстная привязка
Конкретные классы контейнер создаёт сам через рефлексию (auto-wiring), регистрировать нужно интерфейсы, синглтоны и объекты со скалярной конфигурацией.
Фасады
Cache::get('key') это статический прокси: __callStatic достаёт сервис cache из контейнера и вызывает метод. Поэтому фасады подменяются в тестах: Mail::fake(), Queue::fake(), Cache::shouldReceive(...). В своих сервисах лучше внедрять зависимости через конструктор: они видны в сигнатуре.
Middleware, валидация, авторизация
Route::middleware(['auth', 'throttle:60,1'])->group(function () {
Route::post('/orders', [OrderController::class, 'store']);
});
class StoreOrderRequest extends FormRequest
{
public function authorize(): bool { return $this->user()->can('create', Order::class); }
public function rules(): array
{
return ['items' => ['required', 'array', 'min:1'], 'items.*.sku' => ['required', 'string']];
}
}
FormRequest проверяет права и данные до контроллера и возвращает 422 с ошибками. Права на конкретный объект проверяют политики: $this->authorize('update', $order).
Очереди
class SendInvoice implements ShouldQueue
{
use Queueable, SerializesModels;
public int $tries = 5;
public array $backoff = [10, 60, 300];
public function __construct(public Order $order) {}
public function handle(Mailer $mailer): void { ... }
public function failed(Throwable $e): void { ... }
}
SendInvoice::dispatch($order)->onQueue('mail')->afterCommit();
SerializesModelsкладёт в очередь только идентификатор модели, при выполнении она загружается заново.- Задание может выполниться больше одного раза: делайте его идемпотентным.
afterCommit: не ставить задание, пока транзакция не зафиксирована.- Redis + Horizon для мониторинга, отдельные очереди по приоритетам.
- После деплоя
php artisan queue:restart, против утечек--max-jobs,--max-time.
Octane
Octane (Swoole, RoadRunner, FrankenPHP) держит приложение в памяти между запросами. Скорость выше, но статика и синглтоны больше не сбрасываются сами: данные одного пользователя могут утечь в чужой запрос. Используйте scoped и не храните Request в синглтонах.
Деплой
composer install --no-dev -o, php artisan migrate --force, php artisan optimize (кеш конфигурации, маршрутов, представлений), queue:restart.
Частые ошибки
- Логика в контроллерах и моделях вместо сервисов или actions.
$request->all()вcreate.- Синхронная отправка писем в запросе.
- Задания, неидемпотентные при повторе.
env()вне конфигов: послеconfig:cacheон вернётnull.
Что спросят на собесе
- Как проходит запрос через Laravel?
- Как работает сервис-контейнер, чем
bindотличается отsingletonиscoped? - Что такое фасад и как его тестировать?
- Как устроены очереди и как сделать задание надёжным?
- Где валидировать данные и проверять права?