Composer, Updates and Patches
Creating a local patch to a contrib module
In this case, I had the file_entity module installed and wanted to hide the files
tab. That (menu) tab is provided by a task in web/modules/contrib/file_entity/file_entity.links.task.yml
entity.file.collection:
route_name: entity.file.collection
base_route: system.admin_content
title: 'Files'
description: 'Manage files for your site.'
For my patch, I wanted to remove this section of the file_entity.links.task.yml
file.
First I get the repo/git version of the module:
$ composer update drupal/file_entity --prefer-source
Note
You can also just go retrieve the module using the git clone from the module's project page on drupal.org. This allows you to more quickly create your patch without impacting your current project.
Then change the file in the text editor and run git diff to see the changes:
$ git diff
The output shows:
diff --git a/file_entity.links.task.yml b/file_entity.links.task.yml
index 3ea93fc..039f7f9 100644
--- a/file_entity.links.task.yml
+++ b/file_entity.links.task.yml
@@ -15,12 +15,6 @@ entity.file.edit_form:
base_route: entity.file.canonical
weight: 0
-entity.file.collection:
- route_name: entity.file.collection
- base_route: system.admin_content
- title: 'Files'
- description: 'Manage files for your site.'
-
entity.file.add_form:
route_name: entity.file.add_form
base_route: entity.file.add_form
Create the patch:
git diff >file_entity_disable_file_menu_tab.patch
Add the patch to the patches
section of composer.json
under the extra
section. Notice below the line starting with "drupal/file_entity\"
is the local file patch:
"extra": {
"patches": {
"drupal/file_entity": {
"Temporarily disable the files menu tab": "./patches/file_entity_disable_file_menu_tab.patch"
}
"drupal/commerce": {
"Allow order types to have no carts": "https://www.drupal.org/files/issues/2018-03-16/commerce-direct-checkout-50.patch"
},
"drupal/views_load_more": {
"Template change to keep up with core": "https://www.drupal.org/files/issues/views-load-more-pager-class-2543714-02.patch" ,
"Problems with exposed filters": "https://www.drupal.org/files/issues/views_load_more-problems-with-exposed-filters-2630306-4.patch"
},
"drupal/easy_breadcrumb": {
"Titles in breadcrumbs are double-escaped": "https://www.drupal.org/files/issues/2018-06-21/2979389-7-easy-breadcrumb--double-escaped-titles.patch"
},
}
Revert the file in git and then try to apply the patch.
Here is the patch command way to un-apply or revert a patch (-R means revert)
patch -p1 -R < ./patches/fix_scary_module.patch
To apply the patch:
patch -p1 < ./patches/fix_scary_module.patch
For more, see Making a patch.
Patch modules using patches on Drupal.org
Patches can be applied by referencing them in the composer.json file, in the following format. cweagans/composer-patches can then be used to apply the patches on any subsequent website builds.
In order to install and manage patches using composer we need to require the "composer-patches" module:
composer require cweagans/composer-patches
Examples of patches to core look like:
"extra": {
"patches": {
"drupal/core": {
"Add startup configuration for PHP server": "https://www.drupal.org/files/issues/add_a_startup-1543858-30.patch"
}
}
},
"extra": {
"patches": {
"drupal/core": {
"Ignore front end vendor folders to improve directory search performance": "https://www.drupal.org/files/issues/ignore_front_end_vendor-2329453-116.patch"",
"My custom local patch": "./patches/drupal/some_patch-1234-1.patch"
}
}
},
Some developers like adding the actual link to the issue in the description like this:
"extra": {
"patches": {
"drupal/core": {
"Views Exposed Filter Block not inheriting the display handlers cache tags, causing filter options not to appear, https://www.drupal.org/project/drupal/issues/3067937": "https://www.drupal.org/files/issues/2019-07-15/drupal-exposed_filter_block_cache_tags-3067937-4.patch",
"Cannot use relationship for rendered entity on Views https://www.drupal.org/project/drupal/issues/2457999": "https://www.drupal.org/files/issues/2021-05-13/9.1.x-2457999-267-views-relationship-rendered-entity.patch"
},
See Drupal 9 and Composer Patches also Managing patches with Composer
Step by step
- Find the issue and patch in the issue queue on Drupal.org
- Use the title and ID of the issue to be able to locate this post in the future. E.g. Using an issue for the Gin admin theme "Improve content form detection - 3188521"
- Scroll down the issue to find the specific patch you want to apply e.g. for comment #8 grab the file link for
3188521-8.patch
. It is https://www.drupal.org/files/issues/2021-05-19/3188521-8.patch - Add the module name, description and URL for the patch into the extra patches section of json:
"extra": {
"patches": {
"drupal/core": {
"Add startup configuration for PHP server": "https://www.drupal.org/files/issues/add_a_startup-1543858-30.patch"
},
"drupal/gin": {
"Improve content form detection - 3188521": "https://www.drupal.org/files/issues/2021-05-19/3188521-8.patch"
}
}
}
- use
composer update --lock
to apply the patch and watch the output.
If the patch was not applied or throws an error which is quite common (because they are no longer compatible), try using -vvv
(verbose mode) flag with composer to see the reason:
composer update -vvv
Patches from a Gitlab merge request
Use this technique at your peril!
A better process is to download the Merge request patch, and after reviewing carefully, apply it with cweagans/composer-patches
with a local reference like: patches/core-1234567-33.patch
.
Using the URL of the merge request, add .patch
at the end of the URL and that will be the path to the latest patch.
e.g. for a merge request at https://git.drupalcode.org/project/alt_stream_wrappers/-/merge_requests/2 or https://git.drupalcode.org/project/alt_stream_wrappers/-/merge_requests/2/diffs?view=parallel
The patch is at https://git.drupalcode.org/project/alt_stream_wrappers/-/merge_requests/2.patch
Note. The patch file itself may look a little different from what you may be accustomed to. In the example below, you may notice that there is a header with the commit message and author details before the patch itself actually begins. This is normal and the patch should still apply correctly.:
From a4edb6adc09abb1ca52e92d80111173bfa206132 Mon Sep 17 00:00:00 2001
From: Derek Wright <git@dwwright.net>
Date: Wed, 6 Dec 2023 16:42:51 -1000
Subject: [PATCH 1/6] Bug #2418369: Initial Functional tests to show bugs in
URL aliases + language handling
---
.../PathologicLanguageAliasTest.php | 158 ++++++++++++++++++
1 file changed, 158 insertions(+)
create mode 100644 tests/src/Functional/PathologicLanguageAliasTest.php
diff --git a/tests/src/Functional/PathologicLanguageAliasTest.php b/tests/src/Functional/PathologicLanguageAliasTest.php
new file mode 100644
index 0000000..a4f7c84
--- /dev/null
+++ b/tests/src/Functional/PathologicLanguageAliasTest.php
@@ -0,0 +1,158 @@
DANGER
You should not apply patches directly from Gitlab merge requests for several reasons.
- When the new drupal.org is released, issues will be moved to gitlab. These file urls will stop working at some point, and if they do your project won't build. Be future proof, use local patches.
- New patches may be added to the merge request, and you won't know about them. They could be flawed or malicious which could break your site.
- Your composer install (or deployment) now depends on drupal.org. If you are building your site and drupal.org has a temporary outage, your deployment will fail.
composer.json patches in separate file
To separate patches into a different file other than composer json add "patches-file"
section under "extra"
. See example below:
"extra": {
"installer-paths": {
"web/core": ["type:drupal-core"],
"web/libraries/{$name}": ["type:drupal-library"],
"web/modules/contrib/{$name}": ["type:drupal-module"],
"web/profiles/contrib/{$name}": ["type:drupal-profile"],
"web/themes/contrib/{$name}": ["type:drupal-theme"],
"drush/Commands/contrib/{$name}": ["type:drupal-drush"],
"web/modules/custom/{$name}": ["type:drupal-custom-module"],
"web/themes/custom/{$name}": ["type:drupal-custom-theme"]
},
"drupal-scaffold": {
"locations": {
"web-root": "web/"
},
"excludes": [
"robots.txt",
".htaccess"
]
},
"patches-file": "patches/composer.patches.json"
}
Troubleshoot Composer Patches
If composer install fails, try composer -vvv
for verbose output
If the issue is that it can't find the file for example if it displays the following:
- Applying patches for drupal/addtocalendar
./patches/add_to_calendar_smart_date_handling.patch (Add support for smart_date fields)
patch '-p1' --no-backup-if-mismatch -d 'web/modules/contrib/addtocalendar' < '/Users/selwyn/Sites/txglobal/patches/add_to_calendar_smart_date_handling.patch'
Executing command (CWD): patch '-p1' --no-backup-if-mismatch -d 'web/modules/contrib/addtocalendar' < '/Users/selwyn/Sites/txglobal/patches/add_to_calendar_smart_date_handling.patch'
can't find file to patch at input line 5
Perhaps you used the wrong -p or --strip option?
This means the patch is trying to run the patch in the directory web/modules/contrib/addtocalendar
(notice the -d web/modules/contrib/addtocalendar
above
In this case, recreate the patch with the --no-prefix
option i.e.
git diff --no-prefix >./patches/patch2.patch
Then composer install will apply the patch correctly
More at https://github.com/cweagans/composer-patches/issues/146
Stop files being overwritten during composer operations
Depending on your composer.json, files like development.services.yml may be overwritten from during scaffolding. To prevent certain scaffold files from being overwritten every time you run a Composer command you can specify them in the "extra" section of your project's composer.json. See the docs on Excluding scaffold files.
The following snippet prevents the development.services.yml from being regularly overwritten:
"drupal-scaffold": {
"locations": {
"web-root": "web/"
},
"file-mapping": {
"[web-root]/sites/development.services.yml": false
}
},
The code above is from https://www.drupal.org/docs/develop/development-tools/disable-caching#s-beware-of-scaffolding
and from https://www.drupal.org/docs/develop/using-composer/using-drupals-composer-scaffold#toc_6: Sometimes, a project might prefer to entirely replace a scaffold file provided by a dependency, and receive no further updates for it. This can be done by setting the value for the scaffold file to exclude to false. In the example below, three files are excluded from being overwritten:
"name": "my/project",
...
"extra": {
"drupal-scaffold": {
"locations": {
"web-root": "web/"
},
"file-mapping": {
"[web-root]/robots.txt": false
"[web-root]/.htaccess": false,
"[web-root]/sites/development.services.yml": false
},
...
}
}
Modify files that are usually included in scaffolding
Here the composer.json
has a little tweak that inserts (prepends?) the contents of the scaffold/htaccess_prepend.txt
file into the .htaccess
file. This is useful for adding custom rules to the .htaccess
file. It also prevents several other files from being overwritten during composer install or update.
"extra": {
"drupal-scaffold": {
"locations": {
"web-root": "docroot/"
},
"file-mapping": {
"[web-root]/.ht.router.php": false,
"[web-root]/example.gitignore": false,
"[web-root]/INSTALL.txt": false,
"[web-root]/README.md": false,
"[web-root]/web.config": false,
"[web-root]/modules/README.txt": false,
"[web-root]/profiles/README.txt": false,
"[web-root]/sites/default/default.services.yml": false,
"[web-root]/sites/default/default.settings.php": false,
"[web-root]/sites/example.settings.local.php": false,
"[web-root]/sites/example.sites.php": false,
"[web-root]/sites/README.txt": false,
"[web-root]/themes/README.txt": false,
"[web-root]/.htaccess": {
"prepend": "scaffold/htaccess_prepend.txt"
}
}
},
Here are the contents of the scaffold/htaccess_prepend.txt
file. Note that this adds a redirect rule to force HTTPS and also adds .htaccess password control for the dev, test and prod environments.:
<IfModule mod_rewrite.c>
RewriteEngine on
# Redirect HTTP requests to HTTPS.
RewriteCond %{HTTPS} off
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
<IfModule mod_authn_file.c>
# Require authentication for the development environment.
<If "%{ENV:AH_SITE_ENVIRONMENT} == 'dev'">
AuthType Basic
AuthName "Authentication required"
AuthUserFile /mnt/files/abc.dev/nobackup/.htpasswd
Require valid-user
</If>
# Require authentication for the test/stage environment.
<If "%{ENV:AH_SITE_ENVIRONMENT} == 'test'">
AuthType Basic
AuthName "Authentication required"
AuthUserFile /mnt/files/abc.test/nobackup/.htpasswd
Require valid-user
</If>
# Require authentication for the production environment.
<If "%{ENV:AH_SITE_ENVIRONMENT} == 'prod'">
AuthType Basic
AuthName "Authentication required"
AuthUserFile /mnt/files/abc.prod/nobackup/.htpasswd
Require valid-user
</If>
</IfModule>
Updating Drupal Core
if there is drupal/core-recommended
in your composer.json
use:
composer update drupal/core-recommended -W
if there is no drupal/core-recommended
in your composer.json
use:
composer update drupal/core -W
Note composer update -W
is the same as composer update --with-dependencies
Recently, when upgrading from Drupal 10.1.6 to 10.2.3, I used the following command:
composer update "drupal/core-*" --with-all-dependencies
While this updated the core to 10.1.8, it didn't upgrade to 10.2.3. I then used the following command:
composer require drupal/core-recommended:10.2.3 drupal/core-composer-scaffold:10.2.3 drupal/core-project-message:10.2.3 drush/drush --update-with-all-dependencies
Note
I added the drush/drush
part because the first attempt using composer require drupal/core-recommended:10.2.3 drupal/core-composer-scaffold:10.2.3 drupal/core-project-message:10.2.3 --update-with-all-dependencies
failed with:
Problem 1
- Root composer.json requires drupal/core-recommended 10.2.3 -> satisfiable by drupal/core-recommended[10.2.3].
- drupal/core 10.2.3 conflicts with drush/drush <12.4.3.
- drupal/core-recommended 10.2.3 requires drupal/core 10.2.3 -> satisfiable by drupal/core[10.2.3].
- drush/drush is locked to version 12.3.0 and an update of this package was not requested.
More at https://www.drupal.org/project/drupal/releases/10.2.3 and Updating Drupal core via composer updated Dec 2023.
What are the dependencies?
To check why a project is included use composer why
or composer depends
.
composer why enyo/dropzone
drupal/recommended-project dev-master requires enyo/dropzone (^5.9)
Or
composer why drupal/tamper
drupal/recommended-project dev-master requires drupal/tamper (^1.0@alpha)
drupal/feeds_tamper 2.0.0-beta3 requires drupal/tamper (^1.0-alpha3)
You can also see a recursive tree of why the package is depended upon:
composer depends drupal/tamper -t
drupal/tamper 1.0.0-alpha4 Generic plugin to modify data.
├──drupal/recommended-project dev-master (requires drupal/tamper ^1.0@alpha)
└──drupal/feeds_tamper 2.0.0-beta3 (requires drupal/tamper ^1.0-alpha3)
└──drupal/recommended-project dev-master (requires drupal/feeds_tamper ^2.0@beta)
See more about composer why/depends See also this explanation of why-not
Test composer (dry run)
If you want to run through an installation without actually installing a package, you can use --dry-run. This will simulate the installation and show you what would happen.
composer update --dry-run "drupal/*"
produces something like:
Package operations: 0 installs, 4 updates, 0 removals
- Updating drupal/core (8.8.2) to drupal/core (8.8.4)
- Updating drupal/config_direct_save (1.0.0) to drupal/config_direct_save (1.1.0)
- Updating drupal/core-recommended (8.8.2) to drupal/core-recommended (8.8.4)
- Updating drupal/crop (1.5.0) to drupal/crop (2.0.0)
Add a module that isn't currently supported in your version of drupal
For Drupal 9
Install the lenient endpoint:
composer config repositories.lenient composer https://packages.drupal.org/lenient
Your composer.json
file will get this. notice the lenient
key below:
"repositories": {
"lenient": {
"type": "composer",
"url": "https://packages.drupal.org/lenient"
},
"0": {
"type": "composer",
"url": "https://packages.drupal.org/8"
}
},
Specify which Drupal module that composer should be lenient with:
composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/node_access_rebuild_progressive"]'
And composer.json
gets this added:
"drupal-lenient": {
"allowed-list": ["drupal/node_access_rebuild_progressive"]
}
If you haven't already installed the cweagans composer patches plugin use:
composer require cweagans/composer-patches
Create the patch file patches/node_access_rebuild_progressive_d10.patch
with the following contents. It is on drupal.org.
diff --git docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.info.yml docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.info.yml
index 1a0e13eec..f322ff847 100644
--- docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.info.yml
+++ docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.info.yml
@@ -1,7 +1,7 @@
name: 'Node Access Rebuild Progressive'
description: 'Rebuild node access grants in chunks'
type: module
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9.4 || ^10
# Information added by Drupal.org packaging script on 2020-06-23
version: '2.0.0'
diff --git docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.module docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.module
index 45f7c8a41..d2fc50637 100644
--- docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.module
+++ docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.module
@@ -36,7 +36,7 @@ function node_access_rebuild_progressive_trigger() {
node_access_needs_rebuild(FALSE);
// Add default grants in the unlikely case
// no modules implement node_grants anymore.
- if (!count(\Drupal::moduleHandler()->getImplementations('node_grants'))) {
+ if (!count(\Drupal::moduleHandler()->hasImplementations('node_grants'))) {
node_access_rebuild_progressive_set_default();
return node_access_rebuild_progressive_finished();
}
In composer.json add your patch as in below. It is on drupal.org.
"extra": {
"drupal-scaffold": {
"locations": {
"web-root": "docroot/"
},
"file-mapping": {
"[web-root]/sites/development.services.yml": false
}
},
"installer-paths": {
"docroot/core": [
"type:drupal-core"
],
"docroot/libraries/{$name}": [
"type:drupal-library"
],
"docroot/modules/contrib/{$name}": [
"type:drupal-module"
],
"docroot/profiles/contrib/{$name}": [
"type:drupal-profile"
],
"docroot/themes/contrib/{$name}": [
"type:drupal-theme"
],
"drush/Commands/contrib/{$name}": [
"type:drupal-drush"
],
"docroot/modules/custom/{$name}": [
"type:drupal-custom-module"
],
"docroot/profiles/custom/{$name}": [
"type:drupal-custom-profile"
],
"docroot/themes/custom/{$name}": [
"type:drupal-custom-theme"
]
},
"patches": {
"drupal/node_access_rebuild_progressive": {
"Automated Drupal 10 compatibility fixes - 3288770": "patches/node_access_rebuild_progressive_d10.patch"
}
},
Install the module with:
composer require drupal/node_access_rebuild_progressive
The module will be installed and the patch applied!
More at
- Using Drupal's Lenient Composer Endpoint - Sep 2023
- Install a Contributed Module with No Drupal 9 Release - Feb 2023
For Drupal 10
Install the composer lenient plugin composer require mglaman/composer-drupal-lenient
This makes composer.json
look like this:
Notice the require
key and the config
key below
"require": {
"acquia/memcache-settings": "^1.2",
...
"mglaman/composer-drupal-lenient": "^1.0"
},
"conflict": {
"drupal/drupal": "*"
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"allow-plugins": {
"composer/installers": true,
"drupal/core-composer-scaffold": true,
"drupal/core-project-message": true,
"phpstan/extension-installer": true,
"dealerdirect/phpcodesniffer-composer-installer": true,
"php-http/discovery": true,
"cweagans/composer-patches": true,
"mglaman/composer-drupal-lenient": true
},
},
Specify which Drupal module that composer should be lenient with:
composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/node_access_rebuild_progressive"]'
And composer.json
gets this added:
"drupal-lenient": {
"allowed-list": ["drupal/node_access_rebuild_progressive"]
}
If you haven't already installed the cweagans composer patches plugin use:
composer require cweagans/composer-patches
Create the patch file patches/node_access_rebuild_progressive_d10.patch
with the following contents. It is on drupal.org.
diff --git docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.info.yml docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.info.yml
index 1a0e13eec..f322ff847 100644
--- docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.info.yml
+++ docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.info.yml
@@ -1,7 +1,7 @@
name: 'Node Access Rebuild Progressive'
description: 'Rebuild node access grants in chunks'
type: module
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9.4 || ^10
# Information added by Drupal.org packaging script on 2020-06-23
version: '2.0.0'
diff --git docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.module docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.module
index 45f7c8a41..d2fc50637 100644
--- docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.module
+++ docroot/modules/contrib/node_access_rebuild_progressive/node_access_rebuild_progressive.module
@@ -36,7 +36,7 @@ function node_access_rebuild_progressive_trigger() {
node_access_needs_rebuild(FALSE);
// Add default grants in the unlikely case
// no modules implement node_grants anymore.
- if (!count(\Drupal::moduleHandler()->getImplementations('node_grants'))) {
+ if (!count(\Drupal::moduleHandler()->hasImplementations('node_grants'))) {
node_access_rebuild_progressive_set_default();
return node_access_rebuild_progressive_finished();
}
In composer.json add your patch as shown below.
"extra": {
"drupal-scaffold": {
"locations": {
"web-root": "docroot/"
},
"file-mapping": {
"[web-root]/sites/development.services.yml": false
}
},
"installer-paths": {
"docroot/core": [
"type:drupal-core"
],
"docroot/libraries/{$name}": [
"type:drupal-library"
],
"docroot/modules/contrib/{$name}": [
"type:drupal-module"
],
"docroot/profiles/contrib/{$name}": [
"type:drupal-profile"
],
"docroot/themes/contrib/{$name}": [
"type:drupal-theme"
],
"drush/Commands/contrib/{$name}": [
"type:drupal-drush"
],
"docroot/modules/custom/{$name}": [
"type:drupal-custom-module"
],
"docroot/profiles/custom/{$name}": [
"type:drupal-custom-profile"
],
"docroot/themes/custom/{$name}": [
"type:drupal-custom-theme"
]
},
"patches": {
"drupal/node_access_rebuild_progressive": {
"Automated Drupal 10 compatibility fixes - 3288770": "patches/node_access_rebuild_progressive_d10.patch"
}
},
Install the module with:
composer require drupal/node_access_rebuild_progressive
The module will be installed and the patch applied!
More at
- How to incorporate drupal 9-compatible modules into your drupal 10 project - Aug 2023
- https://github.com/mglaman/composer-drupal-lenient
- Using Drupal's Lenient Composer Endpoint - Sep 2023
- Install a Contributed Module with No Drupal 9 Release - Feb 2023
Version constraints
1. The caret constraint (^
): this will allow any new versions except BREAKING ones---in other words, the first number in the version cannot increase, but the others can. drupal/foo:^1.0
would allow anything greater than or equal to 1.0 but less than 2.0.x. If you need to specify a version, this is the recommended method.
2. The tilde constraint (~): this is a bit more restrictive than the caret constraint. It means composer can download a higher version of the last digit specified only. For example, drupal/foo:~1.2 will allow anything greater than or equal to version 1.2 (i.e., 1.2.0, 1.3.0, 1.4.0,...,1.999.999), but it won't allow that first 1 to increment to a 2.x release. Likewise, drupal/foo:~1.2.3 will allow anything from 1.2.3 to 1.2.999, but not 1.3.0.
3. The other constraints are a little more self-explanatory. You can specify a version range with operators, a specific stability level (e.g., -stable or -dev ), or even specify wildcards with *.
Version range: By using comparison operators you can specify ranges of valid versions. Valid operators are >, >=, <, <=, !=.
You can define multiple ranges. Ranges separated by a space ( ) or comma (,) will be treated as a logical AND. A double pipe (||) will be treated as a logical OR. AND has higher precedence than OR.
Note: Be careful when using unbounded ranges as you might end up unexpectedly installing versions that break backwards compatibility. Consider using the caret operator instead for safety.
Examples:
>=1.0
>=1.0 <2.0
>=1.0 <1.1 || >=1.2
Allowing multiple versions
You can use double pipe (||
) to specify multiple version.
For the CSV serialization module the author recommends using the following to install the module:
composer require drupal/csv_serialization:^2.0 || ^3.0
They say: "It is not possible to support both Drupal 9.x and 10.x in a single release of this module due to a breaking change in EncoderInterface::encode()
between Symfony 4.4 (D9) and Symfony 6.2 (D10). When preparing for an upgrade to Drupal 10 we recommend that you widen your Composer version constraints to allow either 2.x or 3.x: composer require drupal/csv_serialization:^2.0 || ^3.0.
This will allow the module to be automatically upgraded when you upgrade Drupal core."
Note
TODO: I couldn't make this work. Anyone want to weigh in on this?
Specify a particular version of PHP
You can specify the version of PHP in composer.json
as shown below:
"config": {
"platform": {
"php": "8.1"
}
},
Here is more of the config section of a composer.json for clarity:
"config": {
"platform": {
"php": "8.1"
},
"allow-plugins": {
"composer/installers": true,
"drupal/core-composer-scaffold": true,
"drupal/core-project-message": true,
"phpstan/extension-installer": true,
"dealerdirect/phpcodesniffer-composer-installer": true,
"php-http/discovery": true
},
"sort-packages": true
},
Levels of stability
- dev: This is the least stable version, and should never be used in production. It often includes packages under active development that may contain bugs or breaking changes and may still undergo significant modifications. It is only recommended for local development purposes.
- alpha: It's a version also undergoing development but in a more stable state. It usually contains fewer breaking changes and features nearing final completion or awaiting a beta release. However, it is also not highly recommended for production environments.
- beta: This version is currently being tested, and minor bugs, when noticed, will need to be fixed. However, it is more stable than the alpha and dev versions, but it's still not recommended for production purposes.
- RC: The RC (Release Candidate) is a version pending official release. It's the closest to being stable, but the version requires community testing and feedback prior to the final release. Undiscovered bugs can also be identified during this phase, so it's best practice not to use it for production purposes.
- stable: This is the required level for production purposes. It includes all packages that have gone through significant changes, bug fixes, community testing, feedback, and is now ready to use.
In your composer.json, you can specify the minimum stability by doing the following:
{
"minimum-stability": "stable"
}
For more:
Composer bump
Composer 2.4 adds a new command called bump, that updates the requirements listed in the composer.json
file with the currently installed version numbers. When the version numbers are bumped in the composer.json
file, it effectively prevents Composer from installing a lower version of the required packages. This can be useful when you have multiple people on a team who are updating composer and have to deal with version conflicts of the composer.lock file. See php.watch article:
To use the bump command with ddev, just run:
ddev composer bump
./composer.json has been updated (46 changes).
Troubleshooting
Composer won't update Drupal core
The composer prohibits
(alias why-not
) command tells you which packages are blocking a given package from being installed. Specify a version constraint to verify whether upgrades can be performed in your project, and if not why not.
Here we ask why won't composer install Drupal version 10.2.3?
composer why-not drupal/core 10.2.3
The output looks like:
drupal/core-recommended 10.1.8 requires drupal/core (10.1.8)
drupal/core 10.2.3 conflicts drush/drush (<12.4.3)
drupal/core 10.2.3 requires symfony/console (^6.4)
drupal/recommended-project dev-main does not require symfony/console (but v6.3.12 is installed)
drupal/core 10.2.3 requires symfony/dependency-injection (^6.4)
drupal/recommended-project dev-main does not require symfony/dependency-injection (but v6.3.12 is installed)
drupal/core 10.2.3 requires symfony/event-dispatcher (^6.4)
drupal/recommended-project dev-main does not require symfony/event-dispatcher (but v6.3.12 is installed)
drupal/core 10.2.3 requires symfony/http-foundation (^6.4)
drupal/recommended-project dev-main does not require symfony/http-foundation (but v6.3.12 is installed)
drupal/core 10.2.3 requires symfony/http-kernel (^6.4)
drupal/recommended-project dev-main does not require symfony/http-kernel (but v6.3.12 is installed)
drupal/core 10.2.3 requires symfony/mime (^6.4)
drupal/recommended-project dev-main does not require symfony/mime (but v6.3.12 is installed)
drupal/core 10.2.3 requires symfony/routing (^6.4)
drupal/recommended-project dev-main does not require symfony/routing (but v6.3.12 is installed)
drupal/core 10.2.3 requires symfony/serializer (^6.4)
drupal/recommended-project dev-main does not require symfony/serializer (but v6.3.12 is installed)
drupal/core 10.2.3 requires symfony/validator (^6.4)
drupal/recommended-project dev-main does not require symfony/validator (but v6.3.12 is installed)
drupal/core 10.2.3 requires symfony/process (^6.4)
drupal/recommended-project dev-main does not require symfony/process (but v6.3.12 is installed)
drupal/core 10.2.3 requires symfony/yaml (^6.4)
drupal/recommended-project dev-main does not require symfony/yaml (but v6.3.12 is installed)
Not finding what you were looking for? Try calling `composer update "drupal/core:10.2.3" --dry-run` to get another view on the problem.
To solve this, I removed drush with composer remove drush/drush
and then ran composer update "drupal/core-*" -W
Then I reinstalled the correct version of drush with composer require drush/drush
which installed version 12.5.1 of drush.
Composer won't install a module
In this case I am trying to install the csv_serialization
module. I get the following error:
composer require 'drupal/csv_serialization:^4.0'
./composer.json has been updated
Running composer update drupal/csv_serialization
Gathering patches for root package.
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- drupal/views_data_export is locked to version 1.3.0 and an update of this package was not requested.
- drupal/views_data_export 1.3.0 requires drupal/csv_serialization ~1.4 || ~2.0 || ~3 -> found drupal/csv_serialization[dev-1.x, dev-2.x, dev-3.x, 1.4.0, 1.5.0, 1.x-dev (alias of dev-1.x), 2.0.0-beta1, ..., 2.x-dev (alias of dev-2.x), 3.0.0-beta1, ..., 3.x-dev (alias of dev-3.x)] but it conflicts with your root composer.json require (^4.0).
Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.
Installation failed, reverting ./composer.json and ./composer.lock to their original content.
So I can try the why-not
command to see why it won't install:
composer why-not drupal/csv_serialization ^4.0
drupal/recommended-project dev-master requires drupal/csv_serialization (^3.0)
drupal/views_data_export 1.3.0 requires drupal/csv_serialization (~1.4 || ~2.0 || ~3)
Not finding what you were looking for? Try calling `composer update "drupal/csv_serialization:^4.0" --dry-run` to get another view on the problem.
So it looks like the drupal/recommended-project
requires drupal/csv_serialization ^3.0
which should not be a problem. Also drupal/views_data_export
requires ~1.4 || ~2.0 || ~3
.
I can try the --dry-run
option to see what happens:
composer update drupal/csv_serialization:^4.0 --dry-run
In UpdateCommand.php line 163:
The temporary constraint "^4.0" for "drupal/csv_serialization" must be a subset of the constraint in your composer.js
on (^3.0)
Well, that's not very helpful. I try updating drupal/views_data_export which succeeds:
composer update drupal/views_data_export
Gathering patches for root package.
Loading composer repositories with package information
Updating dependencies
Lock file operations: 0 installs, 1 update, 0 removals
- Upgrading drupal/views_data_export (1.3.0 => 1.4.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 0 installs, 1 update, 0 removals
- Downloading drupal/views_data_export (1.4.0)
Gathering patches for root package.
Gathering patches for dependencies. This might take a minute.
- Upgrading drupal/views_data_export (1.3.0 => 1.4.0): Extracting archive
- Applying patches for drupal/views_data_export
https://www.drupal.org/files/issues/2021-02-17/2887450-40.patch (Add drush command views-data-export)
...
Now I try to install the module again:
composer require 'drupal/csv_serialization:^4.0'
./composer.json has been updated
Running composer update drupal/csv_serialization
Gathering patches for root package.
Loading composer repositories with package information
Updating dependencies
Lock file operations: 0 installs, 1 update, 0 removals
- Upgrading drupal/csv_serialization (3.0.0 => 4.0.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 0 installs, 1 update, 0 removals
- Downloading drupal/csv_serialization (4.0.0)
Gathering patches for root package.
Gathering patches for dependencies. This might take a minute.
- Upgrading drupal/csv_serialization (3.0.0 => 4.0.0): Extracting archive
Package webmozart/path-util is abandoned, you should avoid using it. Use symfony/filesystem instead.
Generating autoload files
99 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
phpstan/extension-installer: Extensions installed
Found 1 security vulnerability advisory affecting 1 package.
Run "composer audit" for a full list of advisories.
The big reset button
If composer barfs with a bunch of errors, try removing vendor, /core, modules/contrib (and optionally composer.lock using:
$ rm -fr core/ modules/contrib/ vendor/
Then try run composer install again to see how it does:
$ composer install --ignore-platform-reqs
Note --ignore-platform-reqs
is only necessary if your php on your host computer is different to the version in your DDEV containers.
You could always use this for DDEV:
$ ddev composer install
Reference
- Drupal 8 composer best practices - Jan 2018
- Making a patch - Dec 2022
- Composer Documentation
- Composer documentation article on versions and constraints
- Using Drupal's Composer Scaffold updated Dec 2022
- Drupal 9 and Composer Patches by Adrian Vazquez Peligero June 2021
- Managing patches with Composer March 2022
- Utilizing incompatible Drupal 9 modules with Drupal 10 - Aug 2023
- Install a Contributed Module with No Drupal 9 Release - Feb 2023
- Using Drupal's Lenient Composer Endpoint - Sep 2023
- Updating Drupal core via composer updated Dec 2023
- Updating Drupal