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.
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
$PATHor binaries manually (e.g. by creating symlinks from a bin directory already in the
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
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
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
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.
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!