Using composer patches

February 3, 2021

When you use software that is open source, you'll sometimes run into issues or small bugs that have already been fixed by the community in a PR or an issue, but have yet to be merged and/or released. If you're impatient and need that fix now, composer patches can be a solution for this problem.

Using composer patches

You can start using composer packages by installing the cweagans/composer-patches composer package.

 1composer require cweagans/composer-patches

Once this is installed, you can configure it inside your composer.json file under the extra key. But before we can do that, you need a patch to apply!

Getting a patch to apply

The easiest way to get a patch is by getting it from a pull request on Github. Github allows you to download a .diff or .patch file (both are supported by the composer plugin) by requesting the page of the pull request, followed by the file extension. For example:

https://github.com/spatie/ray/pull/266 can be downloaded as a .diff by visiting https://github.com/spatie/ray/pull/266.diff

The PR above looks like this:

 1diff --git a/src/Ray.php b/src/Ray.php
 2index 0c94673..6d1a080 100644
 3--- a/src/Ray.php
 4+++ b/src/Ray.php
 5@@ -94,14 +94,14 @@ public function newScreen(string $name = ''): self
 6         return $this->sendRequest($payload);
 7     }
 8 
 9-    public function clearAll()
10+    public function clearAll(): self
11     {
12         $payload = new ClearAllPayload();
13 
14         return $this->sendRequest($payload);
15     }
16 
17-    public function clearScreen()
18+    public function clearScreen(): self
19     {
20         return $this->newScreen();
21     }
22@@ -271,7 +271,7 @@ public function image(string $location): self
23         return $this->sendRequest($payload);
24     }
25 
26-    public function die($status = '')
27+    public function die($status = ''): void
28     {
29         die($status);
30     }
31@@ -413,7 +413,7 @@ public function pause(): self
32         return $this;
33     }
34 
35-    public function html(string $html = '')
36+    public function html(string $html = ''): self
37     {
38         $payload = new HtmlPayload($html);

Configuring your patch

Once you have the patch file, you can add it to a folder inside your project and configure the patch to be applied. I use <project-root>/patches.

Inside the extra key of your composer.json you can configure all patches

 1"extra": {
 2    "patches": {
 3        "spatie/ray": {
 4            "Add missing return types": "patches/github-pr-266.diff",
 5        }
 6    }
 7},

This tells the composer-patch plugin to patch the spatie/ray package using the patches/github-pr-266.diff file. Every time you now run composer install or composer update, the plugin will re-fetch the package from packagist, and re-apply your patches to it.

You could also link straight to the diff url which will apply the diff in full without having to download the file. But I prefer having more control over the patch as changes to the PR could result in unexpected patches.

Your composer output will contain something along the following:

 1Removing package some/package so that it can be re-installed and re-patched.
 2  - Removing spatie/ray (v1.18)
 3  
 4...
 5
 6Gathering patches for root package.
 7Gathering patches for dependencies. This might take a minute.
 8
 9...
10
11  - Applying patches for spatie/ray
12    patches/github-pr-266.diff (Add missing return types)

If a patch fails, composer will skip the patch and continue by default. If you would like it to fail instead, you can configure it by adding "composer-exit-on-patch-failure": true to the extra key in your composer.json.

Once the PR you need is merged and released, the patch won't do anything anymore and you're free to delete it.

Patches with changes to test files

If your patch includes changes to test files, for example when you've downloaded the patch from a PR that includes changes to tests, your patch might not work when using it on a distributed composer package, as those don't include the tests files most of the time.

In this case there's two options:

  1. Remove the changes to tests from your diff
  2. Adjust the preferred-install of the package you're patching to source:
 1{
 2    "config": {
 3        "preferred-install": {
 4            "spatie/ray": "source"
 5        }
 6    }
 7}
MENU