Drupal 9: 7 Composer Tips

I've been using composer with Drupal for a few years now and I've picked up a few things along the way that have really helped me out. Following on from my post about the anatomy of the recommended Drupal 9 composer file I thought it would be good to expand on that to present some tips.

Here are 7 tips that will help you out when using composer with Drupal.

Automatic Patches With Composer

One of the most useful things I have found is using composer to manage patches to projects. This is possible using a project called composer-patches. You can require this into your project like this.

composer require cweagans/composer-patches

With that in place you can then add patches to the extra/patches section. These take the format a list of projects containing associative array of patch names and paths. The path can be a local file or a remote URL.

...
    "extra": {
        "patches": {
            "drupal/core" : {
                "2975721: Incorrect install.php/rebuild.php redirects": "https://www.drupal.org/files/issues/2020-09-06/drupal-2975721-index-php-redirect-10.patch"
            }
        }
    }
...

Now, when you run composer install, the project will automatically apply the patches you denote in your patches section.

There are a couple of extra options to this. Adding composer-exit-on-patch-failure to the extra section of your composer file will cause cause composer to either continue or fail when a patch can't be applied.

"composer-exit-on-patch-failure": false,

You can also include patches-file setting in order to put your patches into a different file.

 "patches-file": "composer.patches.json",

Adding patches to a different file is useful when working on projects with lots of patches as it prevents cluttering up the main composer.json file.

Composer Security Packages

It's a very good idea to include a couple of packages in your project that will help you with security problems. These are roave/security-advisories and drupal-composer/drpal-security-advisories.

To include roave/security-advisories into your project do the following.

composer require --dev roave/security-advisories:dev-master

To include drupal-composer/drupal-security-advisories into your project to this.

composer require --dev drupal-composer/drupal-security-advisories:8.x-dev

These projects have no functionality of their own, they only create a direct conflict between your project and versions of packages with known security problems. This means that if you try to include one of those packages into your project you will get a conflict warning and composer will refuse to do it. This is a simple and effective means of preventing third party security problems even reaching your codebase.

Validate Your Composer Files

When you change or update your composer.json file in any way you are essentially creating a difference between this file and the composer.lock file. The composer.lock file takes the information in your composer.json file and writes all of the data needed to reconstruct your project again. This will include things like the packages required by the system, what versions of those packages are needed and even any patches applied.

If you want to make sure that your composer.json file is in sync with your composer.lock file you can run the validate command.

composer validate

You'll be wanting to see the following output, which means that your files are in sync.

./composer.json is valid

You should run validate before committing your changes to make sure that anything you push upstream will contain the same information that you expect. If your composer file does not validate there are a couple of things you can do.

First up, if you run composer update with the word "nothing" then composer will resynchronise your lock file, ensure your namespaces are mapped out and resynchronise all of your composer packages. This is probably the command you should try first when re-validating your composer setup as it makes sure that everything is completely in sync.

composer update nothing

Secondly, you can also run composer update with the --lock flag. This will only resynchronise your lock file, but is useful when you need it.

composer update --lock

You might use the --lock flag if you want to ensure your lock file is correct whilst you are performing a merge operation in git. Sorting out merge conflicts in composer is painful, but once you are done running this command will ensure that your composer files match.

Use Composer To Run Commands

Many projects I am part of have custom scripts or actions that need to be run, and these should be documented as much as possible. It is possible, however, to get Composer to run your scripts and provide a handy shortcut so that you don't need to remember a whole load of options to run a command.

As an example, here is the command that I use to run unit tests in a custom module.

./vendor/bin/phpunit ./docroot/modules/custom/my_module/tests/src/Functional/

This can be added to your composer.json file by adding it to a section called scripts. This is an array where the key is the name of the command.

"scripts": {
  "test-module" : "./vendor/bin/phpunit ./docroot/modules/custom/my_module/tests/src/Functional/",
}

This can be run in one of two ways. The first is to use the run-script command that is built into composer.

composer run-script test-module

This is still something to remember, it's much easier to remember than the full command, but you can go a step further. After you synchronise your composer.json file (see composer update nothing above) with your lock file you will also see the script description when you run composer on its own.

$ composer
...
  test-module              Runs the test-tools script as defined in composer.json.
...

This means that you can run the command as a native composer command, like this.

composer test-module

You can also enter multiple commands at once by creating an array structure in the composer file. Here is an example of this in action.

"scripts" : {
    "print-info" : [
        "whoami",
        "uname"
    ]
}

This will run the whoami command (telling you who is logged in) followed by uname command, which will tell you your kernel type.

You can probably guess that this can get out of hand with multiple script entries cluttering up the composer file. My solution to this is to drop these scripts into an external script file that can be used for running a bunch of related commands together.

As en example, I have a command that I use for Drupal development called "nuke", which basically deletes all Drupal and contributed files from across the codebase. Here is the script if you are interested.

# Delete Drupal core and any contrib directories.
rm -rf docroot/core
rm -rf docroot/modules/contrib
rm -rf docroot/themes/contrib

# Delete Drupal root files.
rm -f docroot/.csslintrc
rm -f docroot/.editorconfig
rm -f docroot/.eslintignore
rm -f docroot/.eslintrc.json
rm -f docroot/.gitattributes
rm -f docroot/.gitignore
rm -f docroot/.ht.router.php
rm -f docroot/autoload.php
rm -f docroot/example.gitignore
rm -f docroot/index.php
rm -f docroot/INSTALL.txt
rm -f docroot/robots.txt
rm -f docroot/README.txt
rm -f docroot/update.php
rm -f docroot/web.config

# Delete any Drupal scaffold files.
rm -f .editorconfig
rm -f .gitattributes

# Delete any third party libraries.
rm -rf docroot/libraries

# Delete the vendor directory.
rm -rf vendor

To run this command I added the following to my composer file.

"scripts": {
  "nuke" : "scripts/nuke.sh"
}

Now, if I want to ensure that my codebase is clear of third party code I can just run "composer nuke" and it will remove everything.

Show And Why Commands

I find that when dealing with composer packages that it's really important to understand what packages are installed and, maybe more importantly, why they are installed. This is where two composer commands called show and why come in handy.

The composer show command will tell you about what packages are installed on your system, you can also supply arguments to drill into a particular package. For example, we can ask about the Drupal pathauto module like this.

composer show drupal/pathauto
name     : drupal/pathauto
descrip. : Provides a mechanism for modules to automatically generate aliases for the content they manage.
keywords :
versions : * 1.8.0
type     : drupal-module
license  : GNU General Public License v2.0 or later (GPL-2.0-or-later) (OSI approved) https://spdx.org/licenses/GPL-2.0-or-later.html#licenseText
homepage : https://www.drupal.org/project/pathauto
source   : [git] https://git.drupalcode.org/project/pathauto.git 8.x-1.8
dist     : [zip] https://ftp.drupal.org/files/projects/pathauto-8.x-1.8.zip 8.x-1.8
path     : /Users/philipnorton42/Development/hashbangcode/docroot/modules/contrib/pathauto
names    : drupal/pathauto

support
source : https://cgit.drupalcode.org/pathauto
issues : https://www.drupal.org/project/issues/pathauto
documentation : https://www.drupal.org/docs/8/modules/pathauto

requires
drupal/core ^8.8 || ^9
drupal/ctools *
drupal/token *

suggests
drupal/redirect When installed Pathauto will provide a new "Update Action" in case your URLs change. This is the recommended update action and is considered the best practice for SEO and usability.

As can be seen above, the information returned contains the version installed currently, the dependencies of the package, and any suggested other packages that might be useful. It is also possible to pass in the --available argument to find out the available versions of the module.

composer show drupal/module --available

The why command is useful for finding out why a package is installed. Initially, it might seem strange that we don't know why a package is installed, but remember that composer will install dependencies of the packages you chose to install in your project. This means that you might have multiple packages installed that are not defined in your composer file. The why command will show you exactly why (hence the name) the package is installed.

As an example, let's look at the ctools module. I don't explicitly include this module in my composer file, but it is included in the project so it's a good test to see why it is included.

$ composer why drupal/ctools
drupal/pathauto  1.8.0  requires  drupal/ctools (*)

I normally pass in the -t flag with the why command to show a tree of the dependencies available.

$ composer why -t drupal/ctools
drupal/ctools 3.4.0 Provides a number of utility and helper APIs for Drupal developers and site builders.
└──drupal/pathauto 1.8.0 (requires drupal/ctools *)
   └──hashbangcode/hashbangcode dev-master (requires drupal/pathauto ^1.0)

This tells us that the drupal/ctools project was included by the drupal/pathauto project.

Use Composer Outdated

Whilst the Drupal update module should be used to manage pending updates to your Drupal projects you can also run the composer outdated command to show any pending updates to all packages in your project.

Here is some of the output of the command. I have snipped it in the middle as it is quite lengthy.

$ composer outdated
composer outdated
asm89/stack-cors                     1.3.0              v2.0.1             Cross-origin resource sharing library and stack middleware
composer/semver                      1.5.1              3.2.1              Semver library that offers utilities, version constraint parsing and validation.
consolidation/config                 1.2.1              2.0.0              Provide configuration services for a commandline tool.
consolidation/robo                   2.1.0              2.2.1              Modern task runner
... snip ...
symfony/validator                    v4.4.9             v5.1.6             Symfony Validator Component
symfony/var-dumper                   v5.1.0             v5.1.6             Symfony mechanism for exploring and dumping PHP variables
symfony/yaml                         v4.4.9             v5.1.6             Symfony Yaml Component
twig/twig                            v2.12.5            v3.0.5             Twig, the flexible, fast, and secure template language for PHP
typo3/phar-stream-wrapper            v3.1.4             v3.1.5             Interceptors for PHP's native phar:// stream handling

What can't be seen in the above output is that this list is also color coded to show where you have the latest version (green), where there is an update you can install (yellow) or where there is an update that you will need to edit your composer.json file to be able to include.

Composer Memory Limits

Finally, composer running out of memory is unfortunately quite common. I have done a bit of searching on the internet to solve this over the years and although I have found numerous posts about PHP memory limits and ini files I have rarely gone down that route. In fact, you can solve this problem quite easily, and you don't need to permanently alter any PHP settings.

By prepending your composer call with COMPOSER_MEMORY_LIMIT=-1 you are telling composer to increase the memory limit of it's process to -1, or infinity.

COMPOSER_MEMORY_LIMIT=-1 composer install

To save time you can also add the following to your bash profile so that composer will always have the same memory limit of infinite.

export COMPOSER_MEMORY_LIMIT=-1

Composer version 2.0 has apparently got a lot better at memory management and so these problems shouldn't be present in the new versions. You can use the snippet above even if you do update as nothing it more annoying than running out of memory half way through a critical operation.

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
2 + 8 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.