Composer Global Require Considered Harmful?

Share this article

Composer Global Require Considered Harmful?

Key Takeaways

  • Using ‘composer global require’ for installing packages that are used across various projects is now seen as bad practice by many, unless the globally installed package has no dependencies. This is due to the potential for dependency conflicts when packages share the same space.
  • An alternative solution is to use ‘composer require’ to install each command-line tool to its own local project, managing $PATH or binaries manually. However, this can add a level of complexity and tedium. A proposed change to the global command could see a “global” but isolated project installed to a specific location, with its vendor and bin directories appearing in their usual locations.
  • A new tool, cgr (Composer Global Require), has been developed as an alternative global implementation. It creates isolated installations for each package, avoiding global dependency issues. However, this tool is still in the proof-of-concept stage and may undergo changes. It’s recommended to test it but not to rely on it excessively at this point.

We’ve discussed Composer best practices before, and I’ve always advocated using composer global require when installing packages that can be used across several projects – particularly command line tools. Then, the other day, I ran into this discussion.

Sad Composer

The short of it is – the majority of people now seem to feel like global require is bad practice, unless the globally installed package has zero dependencies. Technically, this makes sense when one is using a single environment for all their projects, but as I commented in that discussion, when using a VM per project or a properly isolated environment like Docker or the like, then this problem is moot and global literally cannot do harm.

The OP’s suggested solution to this problem is:

As an alternative, users should use composer require to install each commandline tool to its own local project, and manage their $PATH or binaries manually (e.g. by creating symlinks from a bin directory already in the $PATH).

This, to me, is an entirely unacceptable complication. Composer has been the pride of PHP in how easy it was to use and how newbie-friendly it made package management – local or global. Having to symlink things around (especially taking into account non-symlink OSs like Windows) would add tedium. The OP then goes further to suggest a change to how the global command works:

a “global” but isolated project could be installed to ~/.composer/global/[something]; its vendor and bin directories would appear in their usual locations, and the contents of the ~/.composer/global/[something]/bin directory could be mirrored (via symlink) in ~/.composer/vendor/bin or, perhaps a better option would be simply ~/.composer/bin. There are various ways that the string [something] could be chosen; the most straightforward would be simply org/project (although this means that long paths such as ~/.composer/global/org/project/vendor/org/project would exist).

I completely agree with this approach, and it seems like the best of both worlds. Obviously, it might cause some backwards-compatibility headaches, but that’s not to say it can’t happen in version 2.0 of Composer. Taylor Otwell echoes this sentiment a bit further below:

Totally agree. It would be amazing to have each composer global installed package installed into its own isolated directory with its own isolated dependencies instead of possibly conflicting with other globally installed packages.

Following all this, in true open-sourcery spirit, the OP then built the alternative global implementation as a separate tool: cgr. Let’s take a look at how it works.

CGR – Composer Global Require Alternative

I’ll be executing all the below commands on a Homestead Improved instance

To start using CGR, we install it as a global package.

composer global require consolidation/cgr

If the bin folder of your Composer isn’t in the PATH variable, add it:

echo "export PATH=\$PATH:\$HOME/.composer/vendor/bin/" >> ~/.bashrc
echo "export CGR_BIN_DIR=\$HOME/.composer/vendor/bin" >> ~/.bashrc
source ~/.bashrc

The above commands extend the $PATH env. variable with the route to Composer’s global bin directory (the default location on Homestead Improved – yours may vary). The second command configures the bin directory for cgr to use, while the third loads these changes. These will also be auto-loaded every time you run the terminal interface as this user (in my case, Vagrant via vagrant ssh).

CGR should then be accessible by just running cgr, and should output Composer’s general help file.

Installing a global Composer package the right way

cgr phpunit/phpunit

On Homestead Improved, there is a useful alias configured where typing phpunit expands into vendor/bin/phpunit which comes in handy when phpunit is installed per-project, so it can be run from the root folder. To test the global installation of PhpUnit, we need to remove this alias first (in ~/.bash_aliases comment the appropriate line) and then exit the shell and re-enter, so the aliases reload. Then, running this newly globally installed PhpUnit with version output should produce something like:

vagrant@homestead:~$ phpunit --version
PHPUnit 5.4.2 by Sebastian Bergmann and contributors.

Now let’s try to install two incompatible packages.

cgr laravel/installer
cgr wp-cli/wp-cli

Sure enough, they both get installed fine. Let’s check if they work.

vagrant@homestead:~$ wp --version
WP-CLI 0.23.1
vagrant@homestead:~$ laravel --version
Laravel Installer version 1.3.2

All good! Global packages that previously conflicted due to mismatches in dependencies can now co-exist side by side and be used OS-wide without a hitch!

What should/can the tool NOT do?

In some cases, you may want to install Composer plugins. As the limitations part states, due to CGR installing each global package into its own folder with its own dependency tree, these would not be globally available across all global projects then. As such, if you want to install plugins that alter composer’s general behavior, you should still use composer global require instead of cgr. CGR itself is one such plugin, for example.

What’s next?

Test, test, test! If you’re a frequent user of the global require command, I urge you to test this new tool and give Greg Anderson some feedback on how well it satisfies your global needs and what could be improved, if anything.

Note that this tool is still just a proof-of-concept, and the implementation may or may not be renamed, repackaged, implemented into Composer’s core eventually, etc. In other words, use it as much as possible, but don’t grow to depend on it too much just yet.

While your global packages are installing, why not tell us how you feel about composer global require? Is it as harmful as many seem to now think, or is it just a matter of being careful and having isolated development environments? Something else? Chime in below!

Frequently Asked Questions about Composer Global Require

Why is using Composer’s global require considered harmful?

Composer’s global require is considered harmful due to the potential for dependency conflicts. When you install packages globally, they all share the same space, which means they share the same set of dependencies. If two packages require different versions of the same dependency, it can lead to conflicts and errors. It’s recommended to install each project with its own set of dependencies to avoid such issues.

What are the alternatives to using Composer’s global require?

Instead of using Composer’s global require, you can create a new Composer project for each tool you need. This way, each tool will have its own set of dependencies, reducing the risk of conflicts. You can also use a tool like cgr, which creates isolated installations for each package, avoiding the global dependency issues.

How does cgr help in avoiding global dependency issues?

CGR, or Composer Global Require, is a tool that creates isolated installations for each package. This means that each package and its dependencies are installed in their own separate directory, avoiding the risk of conflicts between different packages’ dependencies. This makes it a safer alternative to using Composer’s global require.

How can I install and use cgr?

To install cgr, you can use the command composer global require consolidation/cgr. Once installed, you can use cgr just like you would use Composer’s global require. For example, to install a package, you would use the command cgr require package-name.

What is the difference between local and global installation in Composer?

In Composer, a local installation means that the package and its dependencies are installed in the project’s directory. This is the recommended way to install packages, as it avoids dependency conflicts. A global installation, on the other hand, installs the package and its dependencies in a global directory, which can lead to conflicts if different packages require different versions of the same dependency.

How can I manage global dependencies in Composer?

Managing global dependencies in Composer can be challenging due to the risk of conflicts. However, tools like cgr can help by creating isolated installations for each package. You can also manage global dependencies by creating a new Composer project for each tool you need, ensuring that each tool has its own set of dependencies.

Can I use both local and global installations in Composer?

Yes, you can use both local and global installations in Composer. However, it’s recommended to use local installations whenever possible to avoid dependency conflicts. If you need to use a package globally, consider using a tool like cgr to create an isolated installation.

What are the risks of not managing dependencies properly in Composer?

Not managing dependencies properly in Composer can lead to conflicts and errors. If two packages require different versions of the same dependency, it can cause issues that can be difficult to debug. It can also lead to unexpected behavior in your application, as different versions of a dependency may have different features and behaviors.

How can I resolve dependency conflicts in Composer?

To resolve dependency conflicts in Composer, you can try updating your packages to the latest versions, as this may resolve the conflict. If this doesn’t work, you may need to reconsider the packages you’re using and find alternatives that don’t have conflicting dependencies. Tools like cgr can also help by creating isolated installations for each package.

How can I keep my Composer dependencies up to date?

To keep your Composer dependencies up to date, you can use the composer update command. This will update all of your packages to their latest versions, according to the version constraints specified in your composer.json file. You can also use the composer outdated command to see which packages have newer versions available.

Bruno SkvorcBruno Skvorc
View Author

Bruno is a blockchain developer and technical educator at the Web3 Foundation, the foundation that's building the next generation of the free people's internet. He runs two newsletters you should subscribe to if you're interested in Web3.0: Dot Leap covers ecosystem and tech development of Web3, and NFT Review covers the evolution of the non-fungible token (digital collectibles) ecosystem inside this emerging new web. His current passion project is RMRK.app, the most advanced NFT system in the world, which allows NFTs to own other NFTs, NFTs to react to emotion, NFTs to be governed democratically, and NFTs to be multiple things at once.

BrunoScomposerOOPHPPackage Managementpackage managerPHP
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week