Многие из вас наверняка сталкивались с ситуацией, когда в библиотеке или фреймворке, который вы используете, есть баг или нет необходимой функциональности. Предположим, вы даже не поленились и сформировали pull request. Но примут его далеко не сразу, а следующий релиз продукта вообще может произойти через год.  Что же делать, если исправление вам срочно нужно катить в прод? Напрашивается очевидное решение — использовать форк библиотеки или фреймворка. Однако с форками не всё просто. Использовать наследования для переопределения функциональности, которую нужно изменить, не всегда возможно и часто требует больших изменений. На помощь приходят плагины для Composer, которые умеют патчить зависимости. В этой статье я расскажу подробнее о том, почему форки — это неудобно, а также рассмотрю два плагина для Composer для патчинга зависимостей: чем они отличаются, как ими пользоваться и в чём их преимущества. Если вы сталкивались подобными проблемами или вам просто интересно, добро пожаловать под кат.  Проблему удобнее всего рассматривать на примере. Давайте предположим, что мы хотим что-то изменить в библиотеке PHP Code Coverage, которая используется во фреймворке тестирования PHPUnit для измерения уровня покрытия кода тестами. Допустим, мы хотим исправить в версии 7.0.8 как-то так (файл myFix.patch): Создадим нашу библиотеку-пример. Пусть это будет php-composer-patches-example. Детали здесь не очень важны, но на случай если вы решите посмотреть, что из себя представляет библиотека, я привожу консольный вывод под спойлером. Давайте посмотрим, как происходит форк зависимости. Попробуем форкнуть PHP Code Coverage. Но на самом деле всё ещё сложнее: Composer говорит, что установка невыполнима, так как phpunit/phpunit требует phpunit/php-code-coverage версии ^7.0.7, а для нашего проекта требуется dev-7.0.8-myFix: Что с этим делать? Есть четыре варианта: У всех вариантов есть свои недостатки. Я также пробовал использовать тег типа 7.0.8.1, но Composer не принимает такие теги. Второй и четвертый варианты кажутся меньшим из зол. По количеству действий они примерно одинаковые, в этой статье рассмотрим только один — четвертый. Создадим тег альфа-релиза: Допустим, мы хотим использовать нашу библиотеку mougrim/php-composer-patches-example в проекте, который зависит от phpunit/phpunit. Здесь тоже не обойтись без шаманства, придётся опять указать репозиторий https://github.com/mougrim/php-code-coverage.git для phpunit/php-code-coverage, а также явно указать зависимость от phpunit/php-code-coverage версии 7.0.8-a+myFix (иначе установка не завершится успехом): Прошу обратить внимание на то, что php-composer-patches-example подключается как репозиторий, поскольку этот репозиторий является лишь примером и потому не был добавлен в Packagist. В вашем случае этот шаг, скорее всего, можно пропустить. Подведём итоги использования форков. Плюсы этого подхода: Минусы этого подхода: Я думаю, вы уже поняли, что форк зависимости не является такой уж хорошей идеей. В очередной раз испытывая боль и страдания от использования форков, я наткнулся на cweagans/composer-patches в PHP-Дайджесте № 101 (кстати, у pronskiy полезный блог, рекомендую подписаться). Это плагин для Cоmposer, который позволяет применять патчи к зависимостям. Прочитав описание, я подумал, что это именно то, что нужно. Как использовать cweagans/composer-patches: Убеждаемся, что при установке нашей библиотеки в проекте патч тоже применится. Создаём проект: Добавляем в composer.json следущие строки: Устанавливаем mougrim/php-composer-patches-example: Казалось бы, при подключении пакета должна была быть попытка применить патч, но нет.
Обновляем пакеты, чтобы применился патч, но этого не происходит: Порывшись в баг-трекере, я нашёл баг File based patches aren't resolved in dependencies. Получается, нужно либо указывать URL до патча (а значит, скачивать его откуда-то), либо указывать путь до патча вручную в каждом проекте, где вы устанавливаете зависимость, требующую патчей. Подведём итоги использования cweagans/composer-patches. Плюсы этого подхода: Минусы: Последний пункт стал для меня барьером. Сначала я завёл feature request. Мейнтейнер написал, что не хочет добавлять эту фичу в основной код, но во второй версии можно будет написать плагин (да, плагин для плагина для Composer). Перспективы выхода второй версии были туманными, поэтому я решил поискать альтернативы. Среди небольшого списка я не нашёл плагина, который бы поддерживался. Лезть в код плагина не хотелось, поэтому я решил прошерстить форки — наверняка кто-то уже сталкивался с проблемой и решил её. В большинстве форков не было вообще никаких отличий от оригинала (зачем они вообще форкают?). Часть форков была сделана для pull requests, которые уже были слиты с основной библиотекой. Однако всё-таки нашёлся один интересный кандидат, который решал мою проблему, — Vaimo Composer Patches. На тот момент он ещё был оформлен как форк, но его мейнтейнер, похоже, делать pull requests не собирался. Среди прочего, например, он уже поменял имя пакета на vaimo/composer-patches. Но была и проблема: issues были отключены, то есть обратной связи с автором не было вообще. Также плагин не был размещён на Packagist. Такой хороший форк не должен теряться в куче других бесполезных форков. Поэтому я связался с автором с просьбой включить issues и добавить пакет на Packagist. Спустя почти месяц автор ответил и всё это сделал. :) Использование vaimo/composer-patches не отличается от использования предыдущего плагина, но можно указывать разные патчи для разных версий. Убеждаемся, что при установке нашей библиотеки в проекте патч тоже применится. Создаём проект и устанавливаем mougrim/php-composer-patches-example: На всякий случай можно убедиться, что наши изменения применились: Подведём итоги использования vaimo/composer-patches. Плюсы этого плагина почти такие же, как у предыдущего, но ещё включают следующие: Минусы: Подведём общие итоги: Также я сделал косвенный вывод: если какая-то зависимость не предоставляет необходимый функционал, то, возможно, есть форки, которые реализовали этот функционал и даже больше. В Badoo мы используем Vaimo Composer Patches в двух случаях: Ринат Ахмадеев, Sr. PHP Developer UPD1: Спасибо BoShurik за ссылку на алиасы. Добавил пункт про алиасы в статью. Sr. PHP developer