Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Senior: питання на співбесіді з теми «Composer і PSR»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

4 питання

За замовчуванням PSR-4 автозавантажувач для кожного класу обчислює шлях і перевіряє, чи існує файл. На тисячах класів ці перевірки файлової системи помітні.

Рівень 1 - classmap (composer install --optimize-autoloader або -o). Composer заздалегідь будує мапу «клас → файл» для всіх PSR-4/PSR-0 класів. Знайти клас - це один пошук у масиві, який до того ж лежить в OPcache.

Рівень 2 - authoritative classmap (--classmap-authoritative або -a). Те саме, але якщо класу немає в мапі, Composer не шукає його у файловій системі, а одразу вирішує, що класу немає. Швидше для перевірок class_exists() на неіснуючих класах.

Рівень 2/B - APCu (--apcu-autoloader). Знайдені шляхи кешуються в APCu. Альтернатива для випадків, коли authoritative не підходить.

composer install --no-dev --classmap-authoritative

Обмеження authoritative: класи, згенеровані під час виконання (проксі, скомпільовані шаблони в нестандартних місцях), не знайдуться, бо їх не було в мапі під час збирання. Тому його вмикають і перевіряють на стейджингу.

--no-dev теж допомагає: dev-залежності не потрапляють у мапу й не встановлюються на сервер.

Докладніше в документації: Оптимізація автозавантажувача

Це спільні інтерфейси PHP-FIG для роботи з HTTP. Вони дозволяють бібліотекам не залежати від конкретного фреймворку чи HTTP-клієнта:

  • PSR-7 - інтерфейси HTTP-повідомлень: RequestInterface, ResponseInterface, UriInterface, потоки. Об'єкти незмінні: withHeader() повертає нову копію.
  • PSR-17 - фабрики для створення цих об'єктів, щоб бібліотека не викликала new конкретного класу.
  • PSR-15 - серверні обробники й middleware: RequestHandlerInterface, MiddlewareInterface.
  • PSR-18 - HTTP-клієнт: ClientInterface::sendRequest().
final class GithubApi
{
    public function __construct(
        private ClientInterface $http,             // Guzzle, Symfony HttpClient...
        private RequestFactoryInterface $requests,
    ) {}
}

Що це дає:

  • SDK пише код один раз, а застосунок підставляє клієнт, який уже використовує.
  • Middleware, написане за PSR-15, працює в Slim, Mezzio та інших PSR-сумісних фреймворках.
  • У тестах легко підставити фейковий клієнт.

Laravel використовує власні класи Request/Response на базі Symfony HttpFoundation, а не PSR-7. Але конвертувати можна (пакет symfony/psr-http-message-bridge), а Guzzle під HTTP-клієнтом Laravel реалізує PSR-18.

Докладніше в документації: Стандарти PHP-FIG

Composer розв'язує обмеження версій усіх пакетів одночасно. Якщо для потрібного пакета немає версії, сумісної з рештою, він відмовляє з довгим повідомленням «Your requirements could not be resolved».

Інструменти для розслідування:

composer why vendor/package             # хто залежить від пакета і з якими обмеженнями
composer why-not laravel/framework 13.0 # що заважає встановити цю версію
composer outdated --direct              # які прямі залежності застаріли
composer show vendor/package --all      # усі доступні версії
composer update vendor/package -W --dry-run   # що оновиться разом із залежностями

why-not - найкорисніша: показує конкретний пакет і обмеження, що блокує версію. Типова відповідь: «пакет X вимагає illuminate/support ^11.0» - тобто X ще не оновився під нову версію Laravel.

Типові причини й рішення:

  • Старий пакет не підтримує нову версію фреймворку чи PHP. Оновити пакет, знайти форк чи альтернативу, або відкласти оновлення.
  • Залежності залежностей зафіксовані lock-файлом. composer update vendor/package оновлює лише його; -W (--with-all-dependencies) дозволяє оновити й залежності, які йому заважають.
  • Платформа: версія PHP чи розширення на машині не задовольняє require пакета. config.platform.php у composer.json фіксує цільову версію PHP прод-сервера, щоб локально не поставити пакети, які там не запрацюють.
  • Конфлікт conflict-обмежень: пакет явно оголосив несумісність з певними версіями іншого.

Чого не робити: --ignore-platform-reqs на проді «щоб встановилося» - пакети, що вимагають іншої версії PHP чи відсутнього розширення, впадуть під час виконання.

Профілактика: оновлювати залежності регулярно й невеликими порціями (Dependabot, Renovate), а не раз на два роки разом із мажорною версією фреймворку.

Докладніше в документації: Команди depends і prohibits

PSR-11 - мінімальний спільний інтерфейс контейнера залежностей. Усього два методи:

namespace Psr\Container;

interface ContainerInterface
{
    public function get(string $id);   // отримати запис або кинути NotFoundExceptionInterface
    public function has(string $id): bool;
}

Навіщо такий мінімалізм: стандарт описує лише отримання сервісів, а не їхню реєстрацію. Конфігурація (прив'язки, автовпровадження, синглтони) у кожного контейнера своя, а споживання - однакове.

Що це дало екосистемі:

  • Фреймворко-незалежні бібліотеки й фреймворки. Slim, Mezzio, роутери й диспетчери middleware приймають будь-який PSR-11-контейнер: PHP-DI, Symfony DependencyInjection, League Container, Laravel.
  • Laravel реалізує PSR-11: Illuminate\Container\Container - це ContainerInterface, тож сторонні бібліотеки, що очікують PSR-контейнер, працюють з ним.
  • Композиція контейнерів - делегування пошуку іншому контейнеру.

Застереження - Service Locator. PSR-11 прямо не радить передавати контейнер у бізнес-класи, щоб вони самі діставали залежності:

// Погано: прихована залежність, важко тестувати
final class ReportService
{
    public function __construct(private ContainerInterface $container) {}

    public function build(): void
    {
        $mailer = $this->container->get(Mailer::class);
    }
}

// Добре: залежність явна
final class ReportService
{
    public function __construct(private Mailer $mailer) {}
}

Контейнер - для «склеювання» застосунку на верхньому рівні (фабрики, фреймворк), а не для використання всередині логіки. Виняток - фабрики, що за природою створюють різні об'єкти за ідентифікатором.

Докладніше в документації: PSR-11: Container interface