Composer и автозагрузка
До Composer у PHP не было способа сказать «этому проекту нужна вот эта библиотека вот такой версии». Чужой код копировали в репозиторий руками, а PEAR ставил пакеты глобально, на всю машину сразу. Composer поменял это дважды. Во-первых, он сделал набор зависимостей декларативным и пер-проектным: вы объявляете, что вам нужно, он выясняет, какие конкретно версии совместимы между собой, и раскладывает их в vendor/. Во-вторых — и это оказалось важнее — он принёс единое соглашение об автозагрузке. Именно поэтому библиотеки десятка разных вендоров подключаются сегодня одной строкой require 'vendor/autoload.php', а не сотней require на каждый файл.
Из этой пары — «разрешение версий» плюс «автозагрузка» — растут и обе главные ловушки темы, так что назовём их сразу. Первая: composer.json объявляет диапазоны, а composer.lock фиксирует точные версии, которые реально разрешились, включая транзитивные. Lock коммитят, а в CI и на проде запускают install, а не update — иначе воспроизводимость теряется и прод собирается не из того, что вы тестировали. Вторая: автозагрузка не бесплатна. Обычный PSR-4 превращает имя класса в путь и идёт в файловую систему — на каждый впервые встреченный класс, в каждом запросе. В продакшене из этого делают статическую карту классов, и поиск превращается в одно чтение из массива. Разбор по слоям — ниже.
Карта темы
- Composer и его файлы — какую задачу решает менеджер зависимостей, что делает
composer install, что лежит вvendor/и чемrequireотличается отrequire-dev. - composer.lock и semver — что именно фиксирует lock-файл, чем
installотличается отupdateи как на самом деле читаются ограничения^и~. - Пространства имён — как namespace делает имя класса уникальным, что в действительности делает
useи почемуnew Exceptionвнутри namespace ломается. - Автозагрузка PSR-4 — как имя класса превращается в путь на диске, во что это обходится и что меняет оптимизированный classmap.
- Стандарты PSR — что такое PHP-FIG, что стандартизует PSR и как общие интерфейсы отвязывают библиотеку от фреймворка.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Не коммитить composer.lock | Каждая установка заново разрешает версии — прод собирается не из того набора, что прошёл тесты |
Запускать composer update в CI или на деплое | Тянет свежие версии мимо тестов; сборка из зафиксированного набора — это install |
Считать, что ^1.2.3 пустит 2.0.0 | ^ держится внутри мажора — до 2.0.0, не включая; мажор поднимают руками |
Читать ~1.2.3 как «любые обновления» | Тильда с тремя цифрами двигает только патч — до 1.3.0 |
Коммитить vendor/ в git | Это артефакт сборки; его воспроизводят из lock-файла, а не хранят в репозитории |
| Ставить dev-зависимости в продакшен | PHPUnit и генераторы фикстур там не нужны и лишь расширяют поверхность атаки — нужен --no-dev |
| Оставить в проде неоптимизированный автозагрузчик | На каждый новый класс — обращения к файловой системе; --optimize-autoloader сводит их к одному чтению массива |
Включить --classmap-authoritative там, где классы генерируются в рантайме | Классов вне карты для автозагрузчика не существует — Class not found вместо загрузки |
Считать use в начале файла подключением файла | use — алиас времени компиляции; он не загружает ничего |
Писать new Exception внутри namespace | Ищется App\Exception; имена классов не проваливаются в глобальное пространство — нужен \Exception или use |
| Думать, что PSR — часть языка | PSR — договорённость PHP-FIG; движок не проверяет её никогда |
| Менять сигнатуру публичного метода в минорной версии пакета | Потребители с ^ подхватят её на ближайшем composer update и упадут |
Значение для собеседований
Composer — тема, на которой быстро видно, работал ли кандидат в команде и видел ли он прод. Вопрос про composer.lock задают почти всегда, и «это файл, который создаёт Composer» — не ответ. Ответ звучит так: «composer.json — это то, что мы просим, composer.lock — то, что разрешилось, вплоть до транзитивных зависимостей; поэтому lock коммитят, а на проде и в CI вызывают install». Кандидат, который добавляет, что --no-dev уменьшает vendor/, а --optimize-autoloader убирает походы в файловую систему, сразу читается как человек, который сам катал релизы.
Что обычно проверяют:
- Что делает
composer installи чем он отличается отcomposer update. - Что фиксирует
composer.lockи почему его коммитят. - Разницу
^и~и что происходит с^на версиях0.x. - Чем
requireотличается отrequire-devи что даёт--no-devна деплое. - Как PSR-4 отображает полное имя класса на путь и во что это обходится.
- Что такое PHP-FIG и почему PSR-3 или PSR-7 отвязывают библиотеку от фреймворка.
- Как правильно выкатывать ломающее изменение во внутреннем пакете.
Типичный неверный ответ: «composer install и composer update — примерно одно и то же, просто update посвежее». На этом месте разговор обычно и разворачивается: install — это воспроизведение зафиксированного набора версий, update — новое разрешение зависимостей с переписыванием lock-файла. Запустить update на деплое — значит выкатить в прод комбинацию пакетов, которую никто никогда не тестировал. Второй классический провал — уверенность, что ~1.2.3 и ^1.2.3 означают одно и то же.