How to Patch Dependencies Using Composer

If you haven’t worked extensively with Composer, I would recommend reviewing my Absolute Beginner’s Guide to Composer before reading on!

Did you know that you can (and should) be using Composer to manage all of your patches as well as your dependencies? Thanks to a little composer package called cweagans/composer-patches you can add a dependency via composer and then define any patches you need!

This guide is all about how to integrate composer patches into your workflow and how to use this powerful feature of composer!

Why do we patch and is it safe?

In a perfect world, software would always just work and we wouldn’t have to worry about bugs or security updates. Unfortunately, this is the real world and software is ever-evolving. Necessarily this means that things will change, and you may find yourself in a situation where you have a problem that has not been fixed in a stable version of a package / module / dependency that your project relies on.

A recent example from the Govcon project involved the External Link module. In this case, we were seeing PHP warnings:

Warning: implode(): Invalid arguments passed in Drupal\extlink\Form\ExtlinkAdminSettingsForm->buildForm() (line 193 of modules/contrib/extlink/src/Form/ExtlinkAdminSettingsForm.php).

There was an issue posted on Drupal.org but for some time (about 2 weeks) the issue was oustanding and not actually merged into the codebase of the module. In this case, we patched Extlink on Govcon so that the warnings would go away. In this case, we felt that the patch was safe enough to use!

Warning: patches are, by their very nature, experimental in nearly all cases. I strongly recommend that in all cases you:

  • evaluate the stability of the patch (e.g. are there automated tests that cover the source and are those tests passing / failing)

  • is the cure worse than the disease (i.e. can the patch do more damage than whatever it is trying to fix)

  • how active is the issue and what state is it in?

So the short answer is that yes, patches can be safe but not all of them are. Yes you can patch ahead of the release of a given package, just make sure you thoroughly test!

Where to find patches?

Patches typically originate from Git. They fundamentally are used to diff between two states of a file or files. Here’s the example patch from the above issue on Drupal.org. If you examine, the details, it’s a pretty minor patch:

-      '#default_value' => implode(PHP_EOL, $config->get('whitelisted_domains')),
+      '#default_value' => implode(PHP_EOL, (array) $config->get('whitelisted_domains')),

That’s a good thing. Really complex / massive patches are much more difficult to diff and test!

As a general rule though, you can find patches just about anywhere you are using version control. For instance, did you know Github will give you a patch for any pull request? Here’s a recent PR I opened to unlink some fields in a view. All you have to do is slightly modify the URL structure to turn the PR into a patch!

How to add a patch to your composer.json

There are a couple of critical steps (aside from reviewing the patch and testing) to adding a patch to your project.

  1. Make sure you’ve added cweagans/composer-patches as a project dependency (you can do so with $ composer require cweagans/composer-patches if it’s not already in your project)

  2. Confirm what branch / version of the dependency your patch is against. If you’re using the 2.x branch of something and the patch was written against the 4.x branch, it may not apply cleanly

  3. Add the patch!

Adding the patch can be done like this:

"patches": {
            "drupal/extlink": {
                "fix php warnings": "https://www.drupal.org/files/issues/2020-01-31/3110330-2.patch"
        },
  • You will add a new patches key into your composer.json (nested under the extra key). '

  • Register each patch against the dependency that you are changing. In this case, drupal/extlink is a requirement higher up in the composer.json file for Govcon. Note: you can have multiple patches per depedency (assuming they play nicely together), just add a comma and new line for each one (like any other composer new line)

  • Then, you put in some arbitrary text to describe why your’e doing the patch and link to the patch.

  • Finally, you’ll run $ composer update <your package> --with-all-dependencies to allow composer to patch the package and update the composer.lock file with that patch.

  • Commit the changes to your composer.json and composer.lock

Note: you can store patches locally, but I strongly advise against doing so.

What happens when a patch fails

I’ll state this for the record: hopefully all patches will eventually fail! Why? Hopefully it means that the patch has been merged and you don’t need it any longer. My Extlink example in this article is no longer patched on Govcon because it was merged upstream and when the next version of the module was released, we not longer needed the patch (because the new version of the module had the fix). So, I basically followed my steps in the previous section in reverse, removing the patch from composer.json and re-running composer update to remove the requirement for it.

On the other hand, if a patch starts failing and it hasn’t yet been merged, this typically indicates that the package was updated and you may need to re-roll the patch. This is one of the major reasons I advise against local patching. You want as many folks using the fix / advocating for the fix / maintaining the fix as possible. By posting it on an issue queue and making the issue visible, you will hopefully be pitching that problem over the fence so that the package maintainer eventually “fixes” the issue for you! This doesn’t mean you wouldn’t have to reroll a patch (or update to a newer version of a patch) over time, but hopefully this doesn’t happen very often.

If you do need to update to a newer version of the patch, just update the patch link in your composer.json and re-run composer update.

Note that for Drupal issues you can visit the issue queue for the patch in question and watch progress. If new comments with updated versions of the patch are tested / approved by the community you should likely update your site to use the newer version(s) of the patch as they are released.

In Conclusion

Hopefully you don’t have to deal with patches very often. They are a pain in the butt! Having said that, Composer provides a fairly non-painful way to manage the patches for you and if you’re using Composer in your development / devops workflow already, then managing patches along with the rest of your dependencies shouldn’t add any additional overhead.

Related Content