7 Composer Hacks for 2020 and Your Day to Day Workflow

It’s been four years since I started using Composer daily. We have a love hate relationship (mostly love). Over that time, I’ve definitely discovered a few tips and tricks that I wanted to share! So, here are 7 Composer Commands and Hacks that you need to incorporate into your daily workflow.

1. Updating a Single Composer Package (vs. All Packages) and It’s Dependencies

A lot of developers I work with make a super common mistake: they run composer update. Don’t get me wrong, it’s a valid command and it works! But, I want to dig into what exactly composer update does and why it’s a problem.

When you run composer update you are recursively updating every package and every dependency of every package all the way down the dependency tree. Sometimes that is a good thing. But you want to be really smart about when you do it. For instance… a security update? that’s not the right time to update everything.

As a general rule, you should never update “all the things.” I always recommend running the more specific command

composer update <package> --with-all-dependencies

This command will much more specifically target the dependency tree of a single package. The result may still be more than one package updating, but it won’t update “everything” in your project. If the more general composer update command must be run, I try to ensure that an architect or more senior developer is the one running it so that individual can more carefully review changes and understand the impact to the project.

2. Determining If a Composer Package is Present in the Codebase

Because of the way composer draws its dependency trees it’s a safe assumption that you have packages in your project that you haven’t personally required. Here’s a quick example:

One of my codebases has the Pathauto Drupal module in it, even though I didn’t require it. You can use the composer show command to see 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
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/mike.madison/git/d4g/Drupal-GovCon-2017/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.

This command is useful in a couple of ways:

  1. Determining if a package is there and actually getting installed

  2. Determining which specific version of a package is getting installed

I don’t use this command all the time, but when I start troubleshooting or digging into issues, it’s invaluable!

3. Determining Why a Composer Package is Present in the Codebase

In the previous example, I mentioned that the Pathauto module was installed but I didn’t explicitly require it. The composer why command lets you see why something is in your codebase.

composer why drupal/pathauto
drupal/lightning_core  5.2.0  requires  drupal/pathauto (^1.8)  

Again, not a daily command, but one that can really help chase down the dependency tree to understand why you might have something you don’t remember asking for.

4. Determining Why You Can’t Add A Composer Package to your Codebase

Sometimes you try to add a dependency or package and composer rejects the attempt.

composer require drupal/core ^9
    1/8:	https://packages.drupal.org/8/drupal/provider-2018-4$148481c6d868d9f2e9016442914ad1b63344746ed75c4b31f6490097f1ee0f02.json
    2/8:	https://packages.drupal.org/8/drupal/provider-2018-1$deaba284a11adad665abc4e8b4d9da9706bbcd6764eadc3628d46da11cc739c7.json
    3/8:	https://packages.drupal.org/8/drupal/provider-2017-3$3d3419c898476366e46de98002681a3892a023d074c615f2ecced23fee92764c.json
    4/8:	https://packages.drupal.org/8/drupal/provider-2019-1$ab1ffe0d44015aa6ebc9c3e5b64301131b245a9c600af0c182df6a5e288bb5f0.json
    6/8:	https://packages.drupal.org/8/drupal/provider-2019-3$ac530fb16ed26975428a3febf5633816e3e1363ae6d94e9c6ad1b2a270bafc46.json
    6/8:	https://packages.drupal.org/8/drupal/provider-2020-1$0ffc1419e7d9eeea1958da7dba8489a0e288354eb97c23188c489d62a7ea5322.json
    7/8:	https://packages.drupal.org/8/drupal/provider-2019-2$562ff334d7890c05fe4590efae9675fbd05c56362101d0d3cb5398dfab347370.json
    8/8:	https://packages.drupal.org/8/drupal/provider-2020-2$1d52b7f01194cbfaa8236f0a435977eb04f4a333535b33af30f47ebc58972d88.json
    Finished: success: 8, skipped: 0, failure: 0, total: 8
    1/1:	https://asset-packagist.org/p/provider-latest/563924667322c76516b59451a4c35f3213f565175796582cc006d220aaf9960c.json
    Finished: success: 1, skipped: 0, failure: 0, total: 1
    1/12:	http://repo.packagist.org/p/provider-archived$e8fe2f4e5279ec2822f48ed96f17931f1d8ab9e8cf92b9a106777c0e7fb956c0.json
    2/12:	http://repo.packagist.org/p/provider-latest$c169c584683e54fd4aab0b2a2ce88789eca190790a2206fcd852edb21a4c2bf9.json
    3/12:	http://repo.packagist.org/p/provider-2014$ab241402b5015b6d830f925600adfe8370f0a4055cbf84612883b5785d5cdaa4.json
    4/12:	http://repo.packagist.org/p/provider-2019-10$6c17f2684e999e95025752ad6ccc87965fed8acae10c41380e346bbe8dbc2322.json
    5/12:	http://repo.packagist.org/p/provider-2013$eb53dd86c5dd01f01340d8f5aff82d7399b000edf8a9d10b28f93456c271339c.json
    6/12:	http://repo.packagist.org/p/provider-2015$c1c9219c9e736a6ad258d50102a331e3e7e96c6518b34fb16b712af402e627d4.json
    7/12:	http://repo.packagist.org/p/provider-2016$862449b123ae0217a8c5c99dee122adf205705846e5e5418077baef582840275.json
    8/12:	http://repo.packagist.org/p/provider-2020-01$69f4e1e34d776ac200beb1de7fa0273e6456aa341de4b32aa4f0cf47b9938c1b.json
    9/12:	http://repo.packagist.org/p/provider-2017$aca8bb0e280ce632b1ae19eddf307a1d38d8e27f4cda11b54f3dd84bdff52c37.json
    10/12:	http://repo.packagist.org/p/provider-2018$026ac97c7a8ea23c5c2a7c9bc2e54ca69d7fece8d432f91df8b290b4d6ae410f.json
    11/12:	http://repo.packagist.org/p/provider-2020-04$2507d48324250a8e9913fb6523f66cf4b6d278f48e95a4f9dffafd0ea9e51261.json
    12/12:	http://repo.packagist.org/p/provider-2019$4cc4276ee2ba4a58f8dba34e92033930c7ae2daec6ce039a3cffe795454b2a0b.json
    Finished: success: 12, skipped: 0, failure: 0, total: 12
./composer.json has been updated
Gathering patches for root package.
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - drupal/core 8.1.x-dev requires symfony/class-loader ~2.8 -> no matching package found.
    - drupal/core 8.0.x-dev requires symfony/class-loader 2.7.* -> no matching package found.
    - drupal/core 8.3.x-dev requires symfony/class-loader ~2.8 -> no matching package found.
    - drupal/core 8.2.x-dev requires symfony/class-loader ~2.8 -> no matching package found.
    - drupal/core 8.4.x-dev requires symfony/class-loader ~3.2.8 -> no matching package found.
    - Conclusion: remove drupal/better_exposed_filters 4.0.0-beta2
    - Conclusion: don't install drupal/better_exposed_filters 4.0.0-beta2
    - Conclusion: don't install drupal/core 9.0.2
    - Conclusion: don't install drupal/core 9.0.1
    - Conclusion: don't install drupal/core 9.0.0
    - Conclusion: don't install drupal/core 9.0.0-rc1
    - Conclusion: don't install drupal/core 9.0.0-beta3
    - Conclusion: don't install drupal/core 9.0.0-beta2
    - Conclusion: don't install drupal/core 9.0.0-beta1
    - Conclusion: don't install drupal/core 9.0.0-alpha2
    - Conclusion: don't install drupal/core 9.0.0-alpha1
    - Installation request for drupal/better_exposed_filters (locked at 4.0.0-beta2, required as ^4.0-beta1) -> satisfiable by drupal/better_exposed_filters[4.0.0-beta2].
    - Conclusion: don't install drupal/core 9.1.x-dev
    - drupal/better_exposed_filters 4.0.0-beta2 requires drupal/core ^8 -> satisfiable by drupal/core[8.0.x-dev, 8.1.x-dev, 8.2.x-dev, 8.3.x-dev, 8.4.x-dev, 8.5.x-dev, 8.7.x-dev, 8.8.x-dev, 8.9.x-dev].
    - Can only install one of: drupal/core[9.0.x-dev, 8.7.x-dev].
    - Can only install one of: drupal/core[9.0.x-dev, 8.8.x-dev].
    - Can only install one of: drupal/core[9.0.x-dev, 8.9.x-dev].
    - Can only install one of: drupal/core[9.0.x-dev, 8.5.x-dev].
    - Installation request for drupal/core ^9 -> satisfiable by drupal/core[9.0.0, 9.0.0-alpha1, 9.0.0-alpha2, 9.0.0-beta1, 9.0.0-beta2, 9.0.0-beta3, 9.0.0-rc1, 9.0.1, 9.0.2, 9.0.x-dev, 9.1.x-dev].

Potential causes:
 - A typo in the package name
 - The package is not available in a stable-enough version according to your minimum-stability setting
   see <https://getcomposer.org/doc/04-schema.md#minimum-stability> for more details.
 - It's a private package and you forgot to add a custom repository to find it

Read <https://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.

Installation failed, reverting ./composer.json to its original content.

In this example, I tried to require drupal/core ^9 (update to Drupal 9) and it the attempt failed because of many dependencies in my project that are locked in the Drupal 8.x version. The composer why-not command can be very illuminating in this situation!

composer why-not drupal/core ^9
acquia/blt-project             dev-session   requires          drupal/core (~8.9.0)                                       
acquia/blt                     11.4.2        requires          drupal/core (^8.7.0)                                       
acquia/lightning               4.1.5         requires          drupal/core (^8.8.8)                                       
drupal/better_exposed_filters  4.0.0-beta2   requires          drupal/core (^8)                                           
drupal/components              1.1.0         requires          drupal/core (~8.0)                                         
drupal/config_ignore           2.2.0         requires          drupal/core (~8.0)                                         
drupal/config_split            1.4.0         requires          drupal/core (~8.0)                                         
drupal/context                 4.0.0-beta2   requires          drupal/core (~8.0)                                         
drupal/devel                   2.1.0         requires          drupal/core (~8.0)                                         
drupal/drupal-driver           v1.4.0        requires          drupal/core-utility (^8.4)                                 
drupal/flag                    4.0.0-beta1   requires          drupal/core (~8.0)                                         
drupal/honeypot                1.30.0        requires          drupal/core (~8.0)                                         
drupal/libraries               3.0.0-alpha1  requires          drupal/core (~8.0)                                         
drupal/openapi_ui              1.0.0-rc2     requires          drupal/core (~8.0)                                         
drupal/openapi_ui_redoc        1.0.0-rc2     requires          drupal/core (~8.0)                                         
drupal/openapi_ui_swagger      1.0.0-rc3     requires          drupal/core (~8.0)                                         
drupal/panels                  4.4.0         requires          drupal/core (^8.3)                                         
drupal/r4032login              1.x-dev       requires          drupal/core (~8.0)                                         
drupal/recaptcha               2.5.0         requires          drupal/core (~8.0)                                         
drupal/seckit                  1.2.0         requires          drupal/core (~8.0)                                         
drupal/webform                 5.19.0        requires          drupal/core (^8.8)                                         
drupal/webform_views           5.x-dev       requires          drupal/core (^8)                                           
drupal/core                    9.0.0         requires          symfony/console (^4.4)                                     
acquia/blt-project             dev-session   does not require  symfony/console (but v3.4.42 is installed)                 
drupal/core                    9.0.0         requires          symfony/dependency-injection (^4.4)                        
acquia/blt-project             dev-session   does not require  symfony/dependency-injection (but v3.4.42 is installed)    
drupal/core                    9.0.0         requires          symfony/event-dispatcher (^4.4)                            
acquia/blt-project             dev-session   does not require  symfony/event-dispatcher (but v3.4.42 is installed)        
drupal/core                    9.0.0         requires          symfony/http-foundation (^4.4.7)                           
acquia/blt-project             dev-session   does not require  symfony/http-foundation (but v3.4.42 is installed)         
drupal/core                    9.0.0         requires          symfony/http-kernel (^4.4)                                 
acquia/blt-project             dev-session   does not require  symfony/http-kernel (but v3.4.42 is installed)             
drupal/core                    9.0.0         requires          symfony/routing (^4.4)                                     
acquia/blt-project             dev-session   does not require  symfony/routing (but v3.4.42 is installed)                 
drupal/core                    9.0.0         requires          symfony/serializer (^4.4)                                  
acquia/blt-project             dev-session   does not require  symfony/serializer (but v3.4.42 is installed)              
drupal/core                    9.0.0         requires          symfony/translation (^4.4)                                 
acquia/blt-project             dev-session   does not require  symfony/translation (but v3.4.42 is installed)             
drupal/core                    9.0.0         requires          symfony/validator (^4.4)                                   
acquia/blt-project             dev-session   does not require  symfony/validator (but v3.4.42 is installed)               
drupal/core                    9.0.0         requires          symfony/process (^4.4)                                     
acquia/blt-project             dev-session   does not require  symfony/process (but v3.4.42 is installed)                 
drupal/core                    9.0.0         requires          symfony/yaml (^4.4)                                        
acquia/blt-project             dev-session   does not require  symfony/yaml (but v3.4.42 is installed)                    
drupal/core                    9.0.0         requires          twig/twig (^2.12.0)                                        
acquia/blt-project             dev-session   does not require  twig/twig (but v1.43.0 is installed)                       
drupal/core                    9.0.0         requires          symfony-cmf/routing (^2.1)                                 
acquia/blt-project             dev-session   does not require  symfony-cmf/routing (but 1.4.1 is installed)               
drupal/core                    9.0.0         requires          symfony/psr-http-message-bridge (^2.0)                     
acquia/blt-project             dev-session   does not require  symfony/psr-http-message-bridge (but v1.2.0 is installed)  
drupal/core                    9.0.0         requires          laminas/laminas-diactoros (^2.1)                           
acquia/blt-project             dev-session   does not require  laminas/laminas-diactoros (but 1.8.7p2 is installed)       

In this case, we obviously have a lot of blocking dependencies that would prevent the upgrade. Thankfully by upgrading a few specific dependencies (because I happen to know that BLT 11.x and Lightning 4.x aren’t compatible with Drupal 9) I will resolve many of these blockers without having to go all the way down the dependency tree to find the issue(s).

5. Refreshing Your Autoloader

Composer allows you to define autoloading for your project. However, once you’ve written new classes and added them to your codebase, composer may not immediately recognize them without refreshing the autoloader. The composer dump-autoload command will automatically refresh any of the directories and namespaces being monitored by composer to pick up changes in the codebase.

Note: this usually doesn’t trigger any composer.json or composer.lock changes

6. Adding Repositories to Composer that Aren’t in Packagist

When you run a composer require command, composer is using a service (like Packagist) to identify the package in question and locate it on the internet. It might be in Github, Gitlab, Bitbucket, or any other location! However, not all packages that you might want to add are registered to Packagist.

Here are a couple of examples of how to deal with this:

Adding a Custom Packagist

None of the Drupal modules are defined on Packagist. If you attempt to do a composer require drupal/pathauto it will fail unless you have defined the Drupal specific Packagist in your composer. By adding the following to your compser.json though, it will work just fine!

"repositories": {
        "drupal": {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        }
    },

Adding a Custom Repository

You can also define a custom repository that might not be in Packagist (or may not even explicitly be a “composer” package). Common examples for this use case might be:

  • things you are developing that aren’t ready for broad consumption (so they haven’t yet been registered to Packagist)

  • libraries that aren’t explicitly defined as composer libraries (e.g. a javascript library)

In both cases, you can define the repository in your composer.json file and then utilize standard composer methodologies to add the package.

    "repositories": {
        "blt-azure-pipelines": {
            "type": "git",
            "url": "https://github.com/mikemadison13/blt-azure-pipelines"
        }
    }

In this example, my BLT Azure Pipelines package is being directly added (I have since added this to Packagist so this step isn’t required). Once this is defined, all you have to do is define the package in your require array and composer will pull it in just like anything else:

    "require": {
        "mikemadison13/blt-azure-pipelines": "dev-master"
    },

Now, anytime you run a composer install or composer update this package will behave exactly as you might expect (despite the fact that it’s not in Packagist).

You can even use this method to access private git repositories (however, anyone and any tool that will use composer must have a valid ssh key installed that can communicate via git with that repository).

7. Validating And Regenerating Your Composer Files

Finally, composer.json and composer.lock must be valid or they may cause problems. It’s really easy (especially if you aren’t using an IDE) to miss a comma or other bad Json syntax. The composer.lock file also super commonly gets out of whack during git rebases.

The composer validate command will test your composer files and ensure that everything is on the level. If it’s not, you can use the composer update --lock command to regenerate the hashes in the lock file (which will commonly resolve any errors found during composer validate.

I strongly recommend that you automate running of composer validate as part of any code quality checking you’re doing. Ideally, this would run both during continuous integration and any local code validation you do during a git pre-commit hook, etc.

In Conclusion

Composer is an incredibly powerful, but complex tool. I hope that you take the time to get familiar with it and integrate it into your development / devops workflow. I think the power far outweighs the pain!

What are some of your common composer commands that you run?