Sometimes in the course of our work on PHP projects, we may encounter situations such as a PHP Package having bugs or issues that we need urgently fixed.
When that happens what usually occur is 1 of the following 3:
- Edit the files in the vendor folder directly and commit the huge vendor folder
- Fork the package and link to the your forked version in
composer.json
while making a pull request to the original package, hoping the original authors merge your PR soon enough so you don't have to continue maintaining your forked version. - If the web framework you uses support dependency injection, you try to swap the library class with a class of your own via the service container.
However as you may have guessed, many of us would like to avoid approach 1 and 2. Approach 3 while ideal may not be always possible. Having said that there are times where approach 1 and 2 are great too.
So are there other approaches? One that would allow you to patch the official package while allowing you to receive updates to the package in other areas as you wait for your PR to merge?
Composer Patches Packages
I believe the answer lies in composer-patches
packages. In a nutshell, how composer-patches
works is by applying your patch after the package is installed or updated (After you ran composer install
and composer update
In the PHP ecosystem, these 2 packages are the most popular
- https://github.com/cweagans/composer-patches (Recommended by Drupal)
- https://github.com/vaimo/composer-patches (More configuration options)
Let's have a demo of how to use each
cweagans/composer-patches
- Fork the package, create a branch, commit and push your fixes
- Make a pull request to the original package
- While waiting for the package maintainer to merge your changes
- Go to the commit you make and append .patch or .diff to the end of it.
It will look something like this
https://github.com/Laravel-Backpack/CRUD/commit/c474aa4cdc7616ccd9e68e6e823c6d71b2d5a2cb.diff
https://github.com/Laravel-Backpack/CRUD/commit/c474aa4cdc7616ccd9e68e6e823c6d71b2d5a2cb.patch
composer require cweagans/composer-patches
- Add an "extra" key to composer.json with the following
"extra": {
"patches": {
"laravel-Backpack/CRUD": {
"Fix for url generation after save_and_edit": "https://github.com/Laravel-Backpack/CRUD/commit/4521d21ee852061421e403a819fbfca582fa27df.diff"
}
}
}
If you need to add more patches to the same package, Add more key-value pairs under the patches section. For the value, it need not be a github link, it can be a local path in your project as well. As long as it is a diff or patch file.
Now when you run composer update/install, Your patch will be run after the package is installed or updated. Allowing you to continue to get updates to the packages in other parts of the package codebase. And when your PR gets merged, you simply just need to remove your patches entries for that package
vaimo/composer-patches
vaimo/composer-patches
as an alternative, allows you to specify the version constraints the patch should apply to. Here is an example:
After composer require vaimo/composer-patches
, you can do this:
Example below copied from the package readme
{
"extra": {
"patches": {
"some/package": {
"having two patches for same fix": {
">=1.0.0 <1.2.0": "some/path/legacy.patch",
">=1.2.0": "some/path/current.patch"
}
},
"some/other-package": {
"same done for extended patch declaration format": {
"source": {
">=1.0.0 <1.2.0": "some/path/legacy.patch",
">=1.2.0": "some/path/current.patch"
}
}
}
}
}
}
Hope this helps and give you more confidence in using many of the good open source packages out there.