Основы и архитектура

Жизненный цикл запроса, сервис-контейнер, провайдеры, фасады, Artisan.

Junior Что такое Laravel и какие его ключевые особенности?

Laravel — PHP-фреймворк с открытым исходным кодом, построенный по паттерну MVC.

Ключевые особенности:

  • Eloquent ORM — реализация ActiveRecord для работы с БД;
  • Blade — встроенный шаблонизатор;
  • Сервис-контейнер — DI-контейнер для управления зависимостями;
  • Artisan — CLI-инструмент (миграции, генерация кода, очереди);
  • богатая экосистема: очереди, события, планировщик задач, кэш, аутентификация «из коробки»;
  • пакеты первой стороны: Sanctum, Horizon, Telescope, Octane и др.

Философия фреймворка — «convention over configuration»: осмысленные значения по умолчанию и выразительный синтаксис.

Junior Что умеет Artisan? Приведите примеры полезных команд и как создать свою.

Artisan — CLI Laravel (php artisan).

Полезные команды:

php artisan serve                    # встроенный dev-сервер
php artisan migrate / migrate:fresh --seed
php artisan make:model Post -mcr     # модель + миграция + ресурсный контроллер
php artisan tinker                   # REPL с загруженным приложением
php artisan route:list               # все маршруты
php artisan queue:work               # воркер очередей
php artisan config:cache / route:cache / optimize
php artisan down / up                # maintenance-режим

Своя команда:

php artisan make:command SyncReports
class SyncReports extends Command
{
    protected $signature = 'reports:sync {--force}';
    protected $description = 'Синхронизация отчётов';

    public function handle(): int
    {
        $this->info('Start');
        // логика
        return self::SUCCESS;
    }
}

Запуск: php artisan reports:sync --force. Команды также можно планировать через routes/console.php (Scheduler).

Junior Как устроена конфигурация в Laravel и что делает config:cache?

Конфигурация — PHP-файлы в config/, каждый возвращает массив. Доступ: config('app.name'), config('database.default').

Значения берутся из .env через хелпер env('APP_NAME', 'default').

Важно: env() вне конфигов использовать нельзя — после php artisan config:cache .env больше не читается, и env() вернёт null.

Деплой-оптимизация:

php artisan config:cache   # сливает все конфиги в один bootstrap/cache/config.php
php artisan config:clear   # сброс

Кастомный конфиг: создать config/billing.php и читать config('billing.key').

Middle Опишите жизненный цикл HTTP-запроса в Laravel.
  1. Точка входа — public/index.php. Загружается автолоадер Composer, создаётся экземпляр приложения (bootstrap/app.php).
  2. Kernel — запрос передаётся HTTP Kernel (Illuminate\Foundation\Http\Kernel). Kernel регистрирует bootstrapper'ы: загрузка конфигурации, .env, обработка ошибок, фасады, сервис-провайдеры.
  3. Сервис-провайдеры — выполняются методы register() (биндинги в контейнер), затем boot() (роуты, события, view-композеры).
  4. Роутинг — запрос проходит глобальный middleware-стек, затем Router сопоставляет URI с маршрутом.
  5. Middleware маршрута — выполняются middleware группы и конкретного маршрута (web: сессии, CSRF и т.д.).
  6. Контроллер / замыкание — выполняется логика, возвращается Response.
  7. Обратный путь — ответ проходит middleware в обратном порядке и отправляется клиенту. Вызывается terminate() у terminable-middleware (уже после отправки ответа).
Middle Что такое Service Container и как он работает?

Service Container — DI-контейнер, ядро Laravel. Управляет созданием объектов и их зависимостями.

Биндинг:

$this->app->bind(PaymentGateway::class, StripeGateway::class);   // интерфейс → реализация
$this->app->singleton(ReportService::class);                     // один экземпляр на запрос
$this->app->instance('config.custom', $object);                  // готовый экземпляр

Разрешение (resolve):

$service = app(ReportService::class);
$service = resolve(ReportService::class);

Контейнер умеет автовайринг: через рефлексию анализирует type-hint'ы конструктора и рекурсивно создаёт зависимости. Если тип — интерфейс, требуется явный биндинг.

Инъекция работает в конструкторах контроллеров, job'ов, listener'ов, а также в методах контроллеров (method injection).

Middle Чем `bind()`, `singleton()` и `instance()` отличаются в контейнере?
  • bind() — при каждом resolve создаётся новый экземпляр:
    $this->app->bind(Service::class, fn ($app) => new Service($app['config']));
    
  • singleton() — экземпляр создаётся один раз и переиспользуется на протяжении всего жизненного цикла приложения (запроса):
    $this->app->singleton(Cache::class, fn () => new Cache($redis));
    
  • instance() — регистрирует уже созданный объект под ключом:
    $this->app->instance(Service::class, new Service());
    

Практический ориентир: stateless-сервисы — bind; дорогие или stateful (соединение, репозиторий с состоянием) — singleton.

Middle Что такое Service Provider и зачем нужны методы register() и boot()?

Service Provider — место регистрации сервисов приложения. Все провайдеры перечислены в bootstrap/providers.php (Laravel 11+) / config/app.php (старее).

  • register() — только биндинги в контейнер. Нельзя рассчитывать на другие сервисы: порядок загрузки провайдеров не гарантирует их доступность.
  • boot() — вызывается после регистрации всех провайдеров. Здесь можно использовать любые сервисы: регистрация роутов, view-композеров, слушателей событий, валидационных правил.
class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(BillingService::class);
    }

    public function boot(): void
    {
        View::composer('layouts.app', NavComposer::class);
    }
}

Deferred-провайдеры (с $defer = true / DeferrableProvider) загружаются лениво — только когда их сервис реально запрошен.

Senior Что такое фасады? Как они работают «под капотом» и в чём их критика?

Фасад — статический интерфейс доступа к сервису из контейнера.

Как работает: базовый класс Facade определяет __callStatic(), который берёт строку из getFacadeAccessor() (например, 'cache'), резолвит сервис из контейнера и проксирует вызов на реальный объект:

Cache::get('key');
// эквивалентно:
app('cache')->get('key');

Это не статические методы реального класса — поэтому фасады легко мокать в тестах (Cache::shouldReceive(), Event::fake()).

Критика:

  • скрытые зависимости — по сигнатуре класса не видно, что он использует;
  • напоминает глобальное состояние / Service Locator (антипаттерн);
  • затрудняет юнит-тестирование без фреймворка.

Альтернатива — явная инъекция зависимостей через конструктор. В Laravel оба подхода равноправны; фасад — сахар над контейнером.