Defining Patches


You can describe patches to the plugin in one of two ways: the compact format or the expanded format.

Local files are supported too!

In any of the following examples, you can specify a path relative to the root of your project instead of a web address.

Avoid using patches autogenerated by PR/MR URLs

The contents of these patches can change by pushing more commits to a pull request or merge request. A malicious user could abuse this behavior to cause you to deploy code that you didn’t mean to deploy. If you must use a PR/MR as the basis for a patch, download the patch, include it in your project, and apply the patch using the local path instead.

Compact format

 2    [...],
 3    "extra": {
 4        "patches": {
 5            "the/project": {
 6                "This is the description of the patch": "",
 7                "This is another patch": ""
 8            }
 9        }
10    }

This is the format that you may be familiar with from previous versions of Composer Patches. In your composer.json, this is an object with descriptions as keys and patch URLs for values. While this format is more convenient, it has some shortcomings - namely that only a description and URL can be specified. If this works for you/your project, you can keep using this format.

Expanded format

 2    [...],
 3    "extra": {
 4        "patches": {
 5            "the/project": [
 6                {
 7                    "description": "This is the description of the patch",
 8                    "url": ""
 9                },
10                {
11                    "description": "This is another patch",
12                    "url": ""
13                }
14            ]
15        }
16    }

Internally, the plugin uses the expanded format for all patches. Similar to the compact format, the only required fields in the expanded format are description and url. However, in the expanded format, you can specify several other fields:

 2    [...],
 3    "extra": {
 4        "patches": {
 5            "the/project": [
 6                {
 7                    "description": "This is the description of the patch",
 8                    "url": "",
 9                    "sha256": "6f024c51ca5d0b6568919e134353aaf1398ff090c92f6173f5ce0315fa266b93",
10                    "depth": 2,
11                    "extra": {},
12                },
13                {
14                    "description": "This is another patch",
15                    "url": "",
16                    "sha256": "795a84197ee01b9d50b40889bc5689e930a8839db3d43010e887ddeee643ccdc",
17                    "depth": 3,
18                    "extra": {
19                        "issue-tracker-url": ""
20                    }
21                }
22            ]
23        }
24    }

sha256 can either be specified in your patch definition as above or the sha256 of a patch file will be calculated and written to your patches.lock.json file as part of installation.

depth can be specified on a per-patch basis. If specified, this value overrides any other defaults. If not specified, the first available depth out of the following will be used:

  1. A package-specific depth set in package-depths
  2. Any package-specific depth override set globally in the plugin (see cweagans\Composer\Util::getDefaultPackagePatchDepth() for details.)
  3. The global default-patch-depth

extra is primarily a place for developers to store extra data related to a patch, but if you just need a place to put some extra data about a patch, extra is a good place for it. No validation is performed on the contents of extra. The Freeform patcher stores data here.


Patches can be defined in multiple places. With the default configuration, you can define plugins in either composer.json or a patches.json file.


As in previous versions of Composer Patches, you can store patch definitions in your root composer.json like so:

2    [...],
3    "extra": {
4        "patches": {
5            // your patch definitions here
6        }
7    }

This approach works for many teams, but you should consider moving your patch definitions to a separate patches.json. Doing so will mean that you don’t have to update composer.lock every time you change a patch. Because patch data is locked in patches.lock.json, moving the data out of composer.json has little downside and can improve your workflow substantially.

Patches file

If you’re defining patches in patches.json (or some other separate patches file), the same formats can be used. Rather than nesting patch definitions in the extra key in composer.json, the plugin expects patches to be defined in a root-level patches key like so:

2    "patches": {
3        // your patch definitions here
4    }


Packages required by your project can define patches as well. They can do so by defining patches in their root composer.json file. Defining patches in a separate patches.json in a dependency is currently unsupported.

Patch paths are always relative to the root of your project

Patches defined by a dependency should always use a publicly accessible URL, rather than a local file path. Composer Patches will not attempt to modify file paths so that the patch file can be found within the installed location of a dependency.

Duplicate patches

If the same patch is defined in multiple places, the first one added to the patch collection “wins”. Subsequent definitions of the same patch will be ignored without emitting an error. The criteria used for determining whether two patches are the same are:

  • They have the same URL
  • They have the same sha256 hash


If patches.lock.json does not exist the first time you run composer install with this plugin enabled, one will be created for you. Generally, you shouldn’t need to do anything with this file: commit it to your project repository alongside your composer.json and composer.lock, and commit any changes when you change your patch definitions.

This file is similar to composer.lock in that it includes a _hash and the expanded definition for all patches in your project. When patches.lock.json exists, patches will be installed from the locked definitions in this file (instead of using the definitions in composer.json or elsewhere).