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.
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), а не раз на два роки разом із мажорною версією фреймворку.
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) {}
}
Контейнер - для «склеювання» застосунку на верхньому рівні (фабрики, фреймворк), а не для використання всередині логіки. Виняток - фабрики, що за природою створюють різні об'єкти за ідентифікатором.