When I started using Composer I hardly noticed I was using it until it gave me problems. Now that I’ve used Laravel for a while, I’m realising how much I’ve been neglecting Composer! Composer can be super handy and easy to use once you’ve learned a few of its tricks. Here are a few of the things that help me work more effectively with Composer:

Table of Contents

1. Remember the difference between composer install and composer update

This is probably the most important concept you need to understand about Composer. On the surface composer install and composer update look very similar. Both can modify the packages and/or their versions in your vendor folder. However, they have some subtle though very important differences. Here are three different explanations to help you remember their differences:

1.1 TLDR 1

Run composer install when you first create or clone a project, after you’ve switched to another branch or pulled changes from a remote. Run composer update when you want to install new requirements or update your current dependencies to their latest available versions.

1.2 TLDR 2

Run composer install when your composer.lock file has changed or when you don’t have a composer.lock file. Run composer update when the composer.json file has changed or when you want to update your dependencies to their latest available versions.

1.3 Longer explanation

composer install lets you install your initial set of dependencies when no composer.lock file exists. It uses your composer.json file to resolve a set of compatible package versions and installs them into the vendor directory. It will create a composer.lock file which would contain the exact package versions that it has installed.

When the composer.lock file exists, composer install ignores the composer.json file and simply installs the exact same packages and versions that the file contains. Even if your composer.json file contains new package dependencies Composer will ignore them. That is where composer update comes in…

composer update basically does the same that composer install did when there were no composer.lock file yet. In fact, this block in the Composer source shows us that it is actually an “update” that is being done when no composer.lock file exists:

// Force update if there is no lock file present

if (!$this->update && !$this->locker->isLocked()) {
    $this->update = true;

composer update will look at your composer.json file again and resolve the latest versions of all the package requirements, install them to your vendor folder and overwrite your composer.lock file. If you’ve removed any package dependencies from your composer.json it will uninstall them.

One last thing: composer upgrade is just an alias for composer update, so you don’t need to worry about it if you know composer update.

2. Never run Composer as sudo/root

Some composer packages contains scripts that Composer will run, and if you run Composer as sudo these scripts can do anything that you can do as a sudo user. Scary stuff!

Even though Composer shows a warning when you try to run composer as root and has a dedicated FAQ page about it, many people (including my former self!) run composer as root. Unfortunately that FAQ page doesn’t tell noobs like me how to not run it as root, but luckily there’s this really great blog post on how stop running Composer as root.

3. Packagist is a better place to find packages than Google

I used to use Google to find php packages for my projects, but once I found Packagist, I never looked back. Packagist is the main repository for Composer packages and unless you’re using internal company packages, all of your Composer packages would probably be downloaded from Packagist (even if you don’t know about it!). Packagist’s search functionality isn’t half bad and using it to find packages is much more effective than using an ordinary search engine.

Simply search for keywords and Packagist will show you matching packages. The search listings make it easy for you to see how many times each package has been installed. Clicking on a search result takes you to the package’s page where you can see it’s dependencies, README file, available releases and link to it’s source repository.

4. Never modify anything inside the vendor folder

If you edit anything in your vendor directory your edits will be overwritten the next time you run composer update. If you want modify a package (which I highly encourage for learning and contributing purposes!) rather create a fork of the package’s repository and make your changes on a new branch. To find the package’s repository, simply type composer home vendor/package-name in your console. You can add your forked repository as a “repository” in your composer.json file and you can use your branch as the package’s version requirement. That way your team mates can also use your modified version of the package. To install your package, simply run composer update.

If you later create a pull request from your branch to the package’s main repository and it gets merged, you can simply change the version requirement for the package in your composer.json file back to the package’s latest version. Have a look at this StackOverflow question for an example of a scenario like this.

5. You should (almost) always run composer install after changing to another branch or pulling from a remote branch

When you switch branches or pull from a remote branch there’s a chance that the composer.lock file your getting is different from the one you had before. To ensure that your environment is in the same state that the environment on which the commit you checked out was tested on, you need to run composer install. Luckily git hooks can do that for us automatically whenever the composer.lock file has changed.

To check for changes to the composer.lock file and run composer install when you switch branches, simply copy and paste the following code (that I modified from this gist) to a new file called post-merge in your .git/hooks directory in your project:

changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"

check_run() {
  echo "$changed_files" | grep -E --quiet "$1" && eval "$2"

check_run composer.lock 'printf "\nRunning composer install...\n\n" && composer install'

Then run chmod +x .git/hooks/post-merge to make the file executable.

You can copy the following code to .git/hooks/post-checkout and make it executable:

changed_files="$(git diff-tree -r --name-only --no-commit-id $1 $2)"

check_run() {
  echo "$changed_files" | grep -E --quiet "$1" && eval "$2"

check_run composer.lock 'printf "\nRunning composer install...\n\n" && composer install'

6. Always commit your composer.lock file

Often when your composer.lock file has changed it means that you’re using a new package that your code now relies on. If you don’t commit your composer.lock file your code might break on your team mates’ environments!

7. composer require might not always be your friend

composer require vendor/package can be handy when you have to add and install a new dependency to your package. It removes the need for you to manually update your composer.json file and to then run composer update. It even allows you to add multiple packages at once with composer require vendor1/package1 vendor2/package2 etc.. However, sometimes it will tell you that there aren’t an available set of packages available when in fact there is.

It isn’t exactly Composer’s fault that it tells you this, because if you don’t specify a version constraint for the package (using composer require vendor/package:version-constraint) it assumes your want the latest minor version. But sometimes the latest versions aren’t what you need and a noob like me didn’t know that Composer won’t be able to figure that out itself.

Let’s have a look at this question on StackOverflow. The OP wanted to install the mailgun/mailgun-php, php-http/curl-client and guzzlehttp/psr7 packages. While there were a set of mutually compatible versions of the packages available (mailgun/mailgun-php:2.8.1, php-http/curl-client:v1.7.1 and guzzlehttp/psr7:1.5.2), composer require failed and told him “Your requirements could not be resolved to an installable set of packages.” composer require tried to install the latest minor versions of each of the packages which was mailgun/mailgun-php:^2.8, php-http/curl-client:^2.0 and guzzlehttp/psr7:^1.5.

Luckily he didn’t have to go and manually find a compatible set of dependencies himself. He could specify the lowest versions he’d want like this composer require mailgun/mailgun-php:">0" php-http/curl-client:">0" guzzlehttp/psr7:">0" and Composer would find the latest set of versions that would satisfy all of the dependencies.

8. Keep the Composer versions documentation close by to refresh your understanding of package version constraints

This Composer documentation on versions and constraints is very thorough and useful. The two operators that you will often see in composer.json files, but which aren’t very obvious to the untrained eye, is ~ (The tilde version range) and ^ (the caret version range).

The ^ specifies a range where the minimum version is the version specified and the highest allowed version if the next breaking update (in semantic versioning terms). For example ^2.1 allows versions >=2.1 up to <3.0.0, and ^1.1.1 allows versions up to <2.0.0.

The ~ operator is similar, but it basically looks at the last specified digit in the version and allows only it to go up, but not the digits before it. For example ~1.2 can go up to anything below 2.0.0, but ~1.1.2 can only go up to anything below 1.2.

9. Lastly, run composer self-update once in a while

composer self-update is a handy command that updates your Composer installation itself and allows you to use the newest, shiniest version of Composer.