При создании PHP-приложения или библиотеки обычно у вас есть три вида зависимостей: Как управлять этими зависимостями? Жёсткие зависимости: Опциональные зависимости: Зависимости опциональные и связанные с разработкой: И так далее. Что может случиться плохого? Всё дело в ограничениях, присущих require-dev. Зависимости с менеджером пакетов — это прекрасно. Это замечательный механизм для повторного использования кода и лёгкого обновления. Но вы отвечаете за то, какие зависимости и как вы включаете. Вы вносите код, который может содержать ошибки или уязвимости. Вы начинаете зависеть от того, что написано кем-то другим и чем вы можете даже не управлять. Не говоря уж о том, что вы рискуете стать жертвой сторонних проблем. Packagist и GitHub позволяют очень сильно снизить такие риски, но не избавляют от них совсем. Фиаско с left-pad в JavaScript-сообществе — хороший пример ситуации, когда всё может пойти наперекосяк, так что добавление пакетов иногда приводит к неприятным последствиям. Второй недостаток зависимостей заключается в том, что они должны быть совместимы. Это задача для Composer. Но как бы ни был хорош Composer, встречаются зависимости, которые нельзя использовать совместно, и чем больше вы добавляете зависимостей, тем вероятнее возникновение конфликта. Резюме Выбирайте зависимости с умом и старайтесь ограничить их количество. Рассмотрим пример: Эти два пакета — инструменты статичного анализа, при совместной установке они иногда конфликтуют, поскольку могут зависеть от разных и несовместимых версий PHP-Parser. Это вариант «глупого» конфликта: он возникает лишь тогда, когда пытаешься включить зависимость, несовместимую с твоим приложением. Пакеты не обязаны быть совместимыми, ваше приложение не использует их напрямую, к тому же они не исполняют ваш код. Вот другой пример, когда библиотеке предоставляются мосты для работы с Symfony и Laravel. Возможно, чтобы протестировать мосты, вы захотите включить в качестве зависимостей и Symfony, и Laravel: В каких-то случаях это может прекрасно работать, но, скорее всего, будет ломаться. Пример несколько притянутый за уши, потому что очень маловероятно, что какой-то пользователь станет использовать оба пакета одновременно, и ещё менее вероятно, что вам понадобится предусмотреть этот сценарий. Посмотрите на этот composer.json: Здесь кое-что происходит… Можно будет установить компонент Symfony YAML (пакет symfony/yaml) только версий [3.0.0, 4.0.0[. В приложении вам наверняка не будет до этого дела. А вот в библиотеке это может привести к проблеме, потому что у вас никогда не получится протестировать свою библиотеку с symfony/yaml [2.8.0, 3.0.0[. Станет ли это настоящей проблемой — во многом зависит от конкретной ситуации. Нужно иметь в виду, что подобное ограничение может встать поперёк, и выявить это будет не так просто. Показан простой пример, но если требование symfony/yaml: ^3.0 спрятать поглубже в дерево зависимостей, например: вы об этом никак не узнаете, по крайней мере сейчас. KISS. Всё нормально, на самом деле вам этот пакет не нужен! PHAR’ы (PHP-архивы) — способ упаковки приложения в один файл. Подробнее можно почитать об этом в официальной PHP-документации. Пример использования с PhpMetrics, инструментом статичного анализа: Внимание: упакованный в PHAR код не изолируется, в отличие от, например, JAR’ов в Java. Наглядно проиллюстрируем проблему. Вы сделали консольное приложение myapp.phar, полагающееся на Symfony YAML 2.8.0, который исполняет PHP-скрипт: Ваш скрипт myscript.php применяет Composer для использования Symfony YAML 4.0.0. Что может случиться, если PHAR загружает класс Symfony YAML, например Symfony\Yaml\Yaml, а потом исполняет ваш скрипт? Он тоже использует Symfony\Yaml\Yaml, но ведь класс уже загружен! Причём загружен из пакета symfony/yaml 2.8.0, а не из 4.0.0, как нужно вашему скрипту. И если API различаются, всё ломается напрочь. Резюме PHAR’ы замечательно подходят для инструментов статичного анализа вроде PhpStan или PhpMetrics, но ненадёжны (как минимум сейчас), поскольку исполняют код в зависимости от коллизий зависимостей (на данный момент!). Нужно помнить о PHAR’ах ещё кое-что: Одна из самых популярных методик. Вместо того чтобы требовать все зависимости мостов в одном файле composer.json, мы делим пакет по нескольким репозиториям. Возьмём предыдущий пример с библиотекой. Назовём её acme/foo, затем создадим пакеты acme/foo-bundle для Symfony и acme/foo-provider для Laravel. Обратите внимание, мы пока ещё можем всё поместить в один репозиторий, а для других пакетов вроде Symfony использовать репозитории только для чтения. Главное преимущество этого подхода в том, что он относительно прост и не требует дополнительных инструментов, за исключением разделителя по репозиториям вроде splitsh, используемого для Symfony, Laravel и PhpBB. А недостаток в том, что теперь вместо одного пакета вам нужно поддерживать несколько. Можно пойти другим путём и выбрать более продвинутый скрипт установки и тестирования. Для нашего предыдущего примера можно использовать подобное: Работать будет, но, по моему опыту, тестовые скрипты получатся раздутыми и относительно медленными, трудными в поддержке и не слишком простыми в понимании сторонними программистами. Этот подход довольно свежий (в PHP), в основном потому, что раньше не было нужных инструментов, так что я расскажу чуть подробнее. Идея проста. Вместо мы установим phpstan/phpstan и phpmetrics/phpmetrics в разные файлы composer.json. Но тут возникает первая сложность: куда их класть? Какую создавать структуру? Здесь поможет composer-bin-plugin. Это очень простой плагин для Composer, позволяющий взаимодействовать с composer.json в разных папках. Допустим, есть корневой файл composer.json: Установим плагин: После этого, если выполнить composer bin acme smth, то команда composer smth будет выполнена в поддиректории vendor-bin/acme. Теперь установим PhpStan и PhpMetrics: Будет создана такая структура директорий: Здесь vendor-bin/phpstan/composer.json выглядит так: А vendor-bin/phpmetrics/composer.json выглядит так: Теперь можно использовать PhpStan и PhpMetrics, просто вызвав vendor-bin/phpstan/vendor/bin/phpstan и vendor-bin/phpmetrics/vendor/bin/phpstan. Пойдём дальше. Возьмём пример с библиотекой с мостами для разных фреймворков: Применим тот же подход и получим файл vendor-bin/symfony/composer.json для моста Symfony: И файл vendor-bin/laravel/composer.json для моста Laravel: Наш корневой файл composer.json будет выглядеть так: Для тестирования основной библиотеки и мостов теперь нужно создать три разных PHPUnit-файла, каждый с соответствующим файлом автозагрузки (например, vendor-bin/symfony/vendor/autoload.php для моста Symfony). Если вы попробуете сами, то заметите главный недостаток подхода: избыточность конфигурирования. Вам придётся дублировать конфигурацию корневого composer.json в другие два vendor-bin/{symfony,laravel/composer.json, настраивать разделы autoload, поскольку пути к файлам могут измениться, и когда вам потребуется новая зависимость, то придётся прописать её и в других файлах composer.json. Получается неудобно, но на помощь приходит плагин composer-inheritance-plugin. Это маленькая обёртка вокруг composer-merge-plugin, позволяющая объединять контент vendor-bin/symfony/composer.json с корневым composer.json. Вместо получится Сюда будет включена оставшаяся часть конфигурации, автозагрузки и зависимостей корневого composer.json. Ничего конфигурировать не нужно, composer-inheritance-plugin — лишь тонкая обёртка вокруг composer-merge-plugin для предварительного конфигурирования, чтобы можно было использовать с composer-bin-plugin. Если хотите, можете изучить установленные зависимости с помощью Я применял этот подход в разных проектах, например в alice, для разных инструментов вроде PhpStan или PHP-CS-Fixer и мостов для фреймворков. Другой пример — alice-data-fixtures, где используется много разных ORM-мостов для уровня хранения данных (Doctrine ORM, Doctrine ODM, Eloquent ORM и т. д.) и интеграций фреймворков. Также я применял этот подход в нескольких частных проектах как альтернативу PHAR’ам в приложениях с разными инструментами, и он прекрасно себя зарекомендовал. Уверен, кто-то сочтёт некоторые методики странными или нерекомендуемыми. Я не собирался давать оценку или советовать что-то конкретное, а хотел лишь описать возможные способы управления зависимостями, их достоинства и недостатки. Выберите, что вам больше подходит, ориентируясь на свои задачи и личные предпочтения. Как кто-то сказал, не существует решений, есть только компромиссы. ¯\_(ツ)_/¯