Using third-party packages can speed up the development of your project. Sometimes you may need to add extra functionality or fix a critical bug. Here’s how to apply patches to PHP packages installed by Composer.
When to patch a package
First consider whether it is appropriate to create an in-project patch. A patch should always be a minor change. If you need to add extensive new functionality, you must open a problem with the package or split it yourself. This will help avoid conflict as the pack evolves.
You need to think carefully if the change you need is really specific to your project. Dependency patches typically affect just a few lines of code. They fix bugs and problems that would otherwise prevent you from using the package. Most patches are short-lived. They must later be replaced with fixes at the source in the package codebase.
Ultimately, you need to measure your intentions. Are you addressing an immediate issue with a package, or are you extending the package̵
Create a Composer patch
Once you’ve called to patch, you can start preparing your project. Composer doesn’t have built-in patch support, so we’ll cover the popular one simplify/vendor-patches
project to add it. This provides an intuitive interface cweagans/composer-patches
to help you create new patches.
composer require --dev symplify/vendor-patches
Make sure the package you need to patch is installed in your project:
composer require example/broken-package
Then open the problematic file in your code editor. You will find it in it vendor
directory. In our example we need to edit vendor/example/broken-package/src/Broken.php
:
class Broken {
public function __construct(string|int $foo) {
if (is_string($foo)) {
echo "Valid value!";
}
}
}
?>
The Broken
class is supposed to accept both strings and integers, using PHP 8’s union types. If you look at the source it unfortunately turns out that it really only accepts strings.
In our fictional example, the package manager has acknowledged the problem, but has not yet created a new release. In the meantime, let’s get around the problem.
Copy the broken file, unchanged, and add a .old
suffix:
cp vendor/example/broken-project/src/Broken.php vendor/example/broken-project/src/Broken.php.old
Make sure to check it out .old
File!
Then edit it original file so that it functions correctly in your codebase. While you are editing on site, the changes will take effect immediately. Check if your codebase is now behaving as expected.
class Broken {
public function __construct(string|int $foo) {
if (is_string($foo)) {
echo "Valid value!";
}
else if (is_int($foo)) {
echo "Also valid!";
}
}
}
?>
Create the patch file
You can now use the symplify/vendor-patches
project to create a patch file for your fix. The package offers one vendor-patches
binary file that automatically converts your vendor
directory to find the changes you made.
vendor/bin/vendor-patches generate
Running the command will generate a diff for you. It is stored in it patches
directory in the root of your project. The difference is calculated by the .php
and .php.old
files you have created.
You can run the command again to discover new patches that you add. Each modified file gets its own patch within the patches
directory.
Automatic patching
Within you composer.json
file, you will see a new section added:
{ "extra": { "patches": { "example/broken-package": [ "patches/example-broken-package-src-broken-php.patch" ] } } }
The patches
object maps the names of installed packages to a set of patch files to apply. These patches are applied automatically whenever you want composer install
.
An installation script is registered by simplify/vendor-patches
. It is called after each dependency is installed. The script checks if patches have been defined for the package. It automatically applies all found items.
You see lines in it composer install
output indicating when patches were applied. If an error is reported, you can composer install --verbose
for more information on why the patch was skipped. This is often because a package update fixes the problem you have patched, making your patch file redundant.
Delete patches
You can temporarily remove a patch by removing its line from composer.json
. This ensures that the patch is no longer applied composer install
.
To permanently remove a patch from your project, delete it composer.json
line. You can then be .patch
file of your project patches
directory.
If you want to roll back a patch you applied locally, the easiest way is to follow the steps above to remove or disable the patch. You can then remove the package from it vendor
directory and run composer install
to return to a clean slate.
Source link