Cutting the Gordian Knot: bypassing requirements in composer

23 SeptemberSep, 2017
Robert Egginton

A Magento 2 scenario for bypassing requirements in composer

Magento 2 makes heavy use of the composer dependency manager. Which is definitely a good thing – the community had been using it for so many years hacked on to Magento 1 that it was virtually an industry standard. There are times when this can be restrictive though – the meta-package sets all the required versions pretty tightly so there is no way of swapping out components. Is that a problem? Actually, not that often; but it gives us the opportunity to learn some fun things about composer.

If you don’t care about Magento 2 and why this might be important, feel free to skip to the composer bit.

Let’s consider how we make modifications to Magento’s core modules:

Totally Realistic Scenario 1:

You install Magento 2 via the meta-package. This is super easy as you just run composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition ./
So simple, and upgrading consists of changing a single number in the composer.json file – change 2.1.8 to 2.1.9, run composer update, bin/magento setup:upgrade and you’re upgraded! Yes, we like the meta-package.

But then you realise that there’s something wrong with one of the sub-modules. Perhaps there’s a bug (it happens occasionally). Perhaps there is something that urgently needs fixing in magento/module-braintee. The good news is that Magento 2 comes with some powerful plugin and class preference (rewrite) options based around generated plugin instrumentation and dependency injection. So most developers will end up with a MagentoFixes module that adds plugins, events and preferences to fix Magento issues. That’s normally what we do in this situation. No great problem, as long as you remember to document it and revisit them on upgrades.

Totally Realistic Scenario 2:

You install Magento 2 via the meta-package. See above for details.

But one of its dependencies is fixed at a version that you need to change. To pick an example at random (because it totally happened), Magento 2.1.8 requires version 1.6 of the Credis library. But its caching code relies on code from the 1.8 version of the library. So redis caching stops working. Simple solution – pull in version 1.8 of the library…

…except that we’re using the meta-package, which restricts us to version 1.6 🙁

The composer bit

If you try to add a restriction to composer that another requirement disagrees with (e.g. one package requires version 1.6 of a library, but you’ve directly asked for version 1.8) then composer will tie itself in knots trying to satisfy both restrictions and then give up, albeit with a very sensible list of suggestions to fix the issue.

The good news is that composer has a mechanism that lets us bypass any requirement: the replace section of composer.json!

The replace section allow us to list requirements that composer will not pull in itself. Essentially we are telling composer “don’t worry; we’ve got this” and it will leave providing that functionality up to us.

In the above example of replacing credis, we can replace it in two simple steps:

  1. Download Credis 1.8 to somewhere like app/code/Credis/
  2. Add the following sections into your composer.json to tell composer not to bother pulling in credis, and handle the class mapping:
        "replace": {
            "colinmollenhour/credis": "*"
        }
    

    and also the following as part of the “autoload” section as credis is not psr-0 compliant:

        "autoload": {
            ...
            "classmap": [
                "app/code/Credis/Client.php",
                "app/code/Credis/Cluster.php",
                "app/code/Credis/Sentinel.php",
                "app/code/Credis/Module.php"
            ]
        }
    

That’s it! It’s a little bit of a hack, but it’s just really useful to know that if composer ties itself in knots with dependency clashes, you can always cut through the Gordian knot with the sword of replace!

ABOUT THE AUTHOR

Robert Egginton

As our chief problem-solver and systems architect, Rob is involved in every aspect of our development processes. Rob is partial to a bit of improvisational theatre, and setting up a smart home on a budget.