Using composer patches

February 3rd, 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.

composer 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:

diff --git a/src/Ray.php b/src/Ray.php
index 0c94673..6d1a080 100644
- -- a/src/Ray.php
+ ++ b/src/Ray.php
@@ -94,14 +94,14 @@ public function newScreen(string $name = ''): self
         return $this->sendRequest($payload);
     }
 
-     public function clearAll()
+     public function clearAll(): self
     {
         $payload = new ClearAllPayload();
 
         return $this->sendRequest($payload);
     }
 
-     public function clearScreen()
+     public function clearScreen(): self
     {
         return $this->newScreen();
     }
@@ -271,7 +271,7 @@ public function image(string $location): self
         return $this->sendRequest($payload);
     }
 
-     public function die($status = '')
+     public function die($status = ''): void
     {
         die($status);
     }
@@ -413,7 +413,7 @@ public function pause(): self
         return $this;
     }
 
-     public function html(string $html = '')
+     public function html(string $html = ''): self
     {
         $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

"extra": {
    "patches": {
        "spatie/ray": {
            "Add missing return types": "patches/github-pr-266.diff",
        }
    }
},

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:

Removing package some/package so that it can be re-installed and re-patched.
  - Removing spatie/ray (v1.18)
  
...

Gathering patches for root package.
Gathering patches for dependencies. This might take a minute.

...

  - Applying patches for spatie/ray
    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:

{
    "config": {
        "preferred-install": {
            "spatie/ray": "source"
        }
    }
}

You can like or retweet this Tweet
MENU