Introduction
Dependencies are essential for modern development. They save you time and energy. Functionalities you may need for your app like sending e-mails or logging can all be easily included as third party libraries. Thanks to the open source movement, there are many high quality packages to choose from. In the early days, including third-party libraries was cumbersome and error prone but luckily, today we have tools like Composer to help us.
Composer is an exceptional dependency manager for PHP. It replaces PEAR and rightfully so. PEAR requires that your project is specially prepared to work with it, where Composer gives you all the freedom you need without any special requirements. One major difference between these two tools is that PEAR installs dependencies globally and Composer installs them locally, in your project structure. PEAR is essentially a package manager and Composer is a dependency manager. That’s where the emphasis is.
Composer was inspired by projects like NPM and Bundler. The vast selection of compatible packages are hosted on the official Composer repository called Packagist. These packages are open source so you can contribute to them too. Popular frameworks and tools like Laravel, PHPUnit and Monolog can all be found here. You can even use a specific code revision of the package when including it in your project so you are getting great flexibility. Composer packages are versioned, so you can pin down the exact version of the package you need. This makes porting your project to another machine or to a CI service such as [Semaphore] (https://semaphoreci.com) effortless.
In this tutorial, we’ll explore some of the most used Composer features and show how to use them. After following through, you should be comfortable with managing your PHP project’s dependencies with Composer.
Prerequisites
The software you need is as follows:
- Some version of PHP 5, preferably the latest. Composer is compatible with PHP versions 5.3.2 and up.
- Clients for Git and Subversion
I’ll be using PHP version 5.6.5, but it’s perfectly fine to use any other version you have as long as it’s newer than 5.3.2. If you don’t have PHP installed, you can do it with phpbrew which makes managing multiple PHP versions on the same machine easy.
Installation
Composer can be installed in two different ways.
Install Locally
Local installation will download composer.phar
to the current directory. The drawback of this method is that you’ll always have to reference the Composer’s executable from the directory where it’s downloaded to.
$ curl -sS https://getcomposer.org/installer | php
All settings correct for using Composer
Downloading...
Composer successfully installed to: /workspace/composer.phar
Use it: php composer.phar
$ php composer.phar --version
Composer version 1.0-dev (1d8f05f1dd0e390f253f79ea86cd505178360019)
Install Globally (Recommended)
Installing Composer globally is a handy way to have access to the tool from anywhere by just executing the composer
command.
$ curl -sS https://getcomposer.org/installer | php
All settings correct for using Composer
Downloading...
Composer successfully installed to: /workspace/composer.phar
Use it: php composer.phar
$ sudo mv composer.phar /usr/local/bin/composer
$ composer --version
Composer version 1.0-dev (1d8f05f1dd0e390f253f79ea86cd505178360019)
If you installed PHP with phpbrew
running the command below is sufficient
phpbrew install-composer
We continue this tutorial with the assumption that Composer has been installed globally.
Configuring Composer
Composer is configured with a single file named composer.json
located in the root directory of the project. To follow along, just create an empty directory called composer-tutorial, fire up a text editor and create an empty composer.json
file in this directory. Here’s how the most simple Composer file looks like:
{
"require": {
"symfony/yaml": "2.6.4"
}
}
The only requirement other than the fact that the file has to be in a JSON format, is the inclusion of the require
key. This key defines your project’s dependencies with a list of packages and their respective versions.
Defining Dependencies
By convention, package names consist of the package’s vendor and its project name. This is done in an effort to avoid name conflicts. In our example above, the vendor is symfony
and the project is called yaml
.
The version of the package can be defined in multiple ways. This is where Composer gives you huge flexibility.
2.6.4
, locks the package to the exact version defined>= 2.6.4
, defines a range where the package’s version has to be at least2.6.4
but a newer version is used if it’s available. Operators like<
,<=
,>
,>=
and!=
can also be used.2.6.*
, which is equivalent to>=2.6 <2.7
.~2.6
, is used for projects which use semantic versioning and is equivalent to>=2.6 <3.0
. It essentially allows the micro version to change:~2.6.4
is the same as saying>=2.6.4 <2.7.0
.^2.6
, caret is used when we want to avoid braking updates for projects which employ semantic versioning.^2.6
is equivalent to>=2.6.4 <3.0.0
.
A more detailed description of these restrictions can be found on [this page] (https://getcomposer.org/doc/01-basic-usage.md#package-versions).
Breaking Out of the Defaults
If you’re not satisfied with the default settings, you can use the config
key and customize anything from specifying Composer’s default installation path to defining GitHub protocols to use. For example, to define a custom installation path for our Composer packages, we alter our composer.json
to look like this:
{
"config": {
"vendor-dir": "dependencies"
},
"require": {
"symfony/yaml": "2.6.4"
}
}
A full list of config
options can be found here.
Custom package sources
A package is essentially a directory containing information like the version of the package and the source from where to get the contents of the package. Two types of package sources exist:
dist
, a packaged version, tested and stablesource
, clones the contents of the package from a version control system
Packages from source
are usually used in development and dist
packages tend to be production ready.
Composer by default uses dist
packages from Packagist. However, this can be easily expanded by using the repositories
key and including source
packages. You can use sources like version control systems (Git, SVN and Hg) or even PEAR packages. To add the monolog
library directly from GitHub, the following has to be done:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/Seldaek/monolog"
}
],
"require": {
"symfony/yaml": "2.6.4",
"monolog/monolog": "1.12.0"
}
}
Repositories are a list of versions for the package. We require a version from the repository the same way we do with dist
packages.
Refer to this page for an in-depth explanation of the various options for configuring repositories.
Installing dependencies
To install the dependencies you defined in composer.json
, we use the following command:
composer install
The installation process will fetch the latest packages according to the constraints we defined. The packages are put in the vendor
directory or to a custom directory like we defined earlier. The installation command has a couple of interesting options.
--prefer-source
will install the packages from their source which is usually a GitHub or a Subversion repository. In other words, it clones the package’s source. In the case where the repository is not found on the vcs, it falls back to the installation fromdist
.--prefer-dist
prefers to install packages from Packagist and caches the package archives locally.--profile
will give you statistics about the installation at the end of the process. It can be used with any Composer command.
Caching dependencies
Composer stores its cache in $COMPOSER_HOME/cache
which is usually set to ~/.composer/cache
by default. Caching is very beneficial when there are multiple projects which have some dependencies in common. The first time composer install
is executed, it downloads and caches the dependencies which are not present in the cache. The next time it’s initiated, it’ll load the installed packages from cache.
Let’s see what difference does the installation from cache can make.
user@host:~/workspace/project-1/$ composer install --prefer-dist --profile
[6.5MB/0.03s] Loading composer repositories with package information
[6.8MB/0.48s] Installing dependencies (including require-dev)
[47.0MB/12.45s] - Installing symfony/yaml (v2.6.4)
[47.1MB/13.70s] Downloading: 0%[47.1MB/13.71s]
...
[47.1MB/13.93s] Downloading: 100%[47.1MB/13.93s]
[46.8MB/14.37s] Writing lock file
[46.8MB/14.37s] Generating autoload files
[46.8MB/14.37s] Memory usage: 46.77MB (peak: 56.15MB), time: 14.37s
Here’s what happens when a cached packaged is reused for another installation:
user@host:~/workspace/project-2/$ composer install --prefer-dist --profile
[6.1MB/0.03s] Loading composer repositories with package information
[6.4MB/0.41s] Installing dependencies (including require-dev)
[46.5MB/3.65s] - Installing symfony/yaml (v2.6.4)
[46.9MB/3.65s] Loading from cache
[47.0MB/3.68s]
[46.6MB/4.07s] Writing lock file
[46.6MB/4.07s] Generating autoload files
[46.6MB/4.07s] Memory usage: 46.62MB (peak: 55.65MB), time: 4.07s
As you can see, installing cached packages is much faster the second time. This makes a big difference in a project with many dependencies.
Locking down dependencies
When the installation process is initiated the first time, it creates a composer.lock
file. This file locks down all the currently installed packages with their versions and dependencies. It’s important to include this file to the version control system, because this is what makes your project transferable to other machines. This way you can rest assured that anyone who contributes to the project will have the same dependencies installed as you.
Updating dependencies
Frameworks and libraries are being improved all the time and if we want to get the latest features and bugfixes, our dependencies need to be kept up to date. This can be achieved simply by using the update
command.
composer update
The update
command will install all the latest packages while taking into consideration the version constraints defined in composer.json
and it also updates the composer.lock
file.
Updating all the dependencies however isn’t always optimal. Maybe there’s a breaking change in one of the updates and you want to keep the old version until you handle it later. Composer packages can be updated one-by-one by letting the update
command which vendor/project
you want updated.
composer update symfony/yaml
The update command has very similar options to install
and they can be found here.
During your time with installing and updating Composer packages, you might meet this error message:
Warning: The lock file is not up to date with the latest changes in
composer.json, you may be getting outdated dependencies, run update to update
them.
This happens when even a tiniest adjustment is introduced in composer.json
. Like changing the author, description or even altering a single letter. The slightest modification is enough to change the file’s MD5 hash which is detected by composer.lock
. To avoid running the update
when a trivial modification occurs, the --lock
option is used to suppress the warning and update the lock file.
composer update --lock
Autoloading
You may have noticed that Composer is generating autoload files after the packages are successfully installed or updated. More specifically, the file in question is vendor/autoload.php
. Autoloading makes it really convenient to use the available dependencies throughout your project by simply referencing their class names without the need to explicitly require the files where the classes reside. The only thing that needs to be included in your project is the autoload file.
Composer currently supports these autoloading mechanisms:
There’s even an option to autoload your own classes which we will demonstrate next.
Autoloading a custom class
Let’s see a simple example about how autoloading works. In our root directory composer-tutorial
, create a directory called source
. In this newly created directory, create a file called Square.php
.
<?php
namespace Shapes;
class Square {
static function area($side){
$surface_area = $side * $side;
echo "The squares surface area is $surface_area units\n";
}
}
The area
function calculates the square’s surface area according to its side.
Add the autoload
key to composer.json
where we define the mapping between the namespace and the path. Composer will look for files located in ./source
which belongs to the /Shapes
namespace.
// composer.json
{
"autoload": {
"psr-4": {"Shapes\\": "source/"}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/Seldaek/monolog"
}
],
"require": {
"symfony/yaml": "2.6.4"
}
}
After the custom class is included the autoload file has to be regenerated to reflect the changes.
composer dump-autoload
You can see the effect of dump-autoload
in vendor/composer/autoload_psr4.php
.
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Shapes\\' => array($baseDir . '/source'),'
)
Now, let’s create a simple command line script which will use the Square
class by just requiring vendor/autoload.php
and nothing more. Create this file in the root directory (composer-tutorial
) and name it calculate_area.php
.
#! /usr/bin/env php
<?php
require_once __DIR__ . '/vendor/autoload.php';
$side = isset($argv[1]) ? $argv[1] : null;
if (isset($side) && ctype_digit($side)) {
Shapes\Square::area($side);
}
else {
echo "Please provide a valid size!\n";
}
After the file is created, make it executable and run it.
$ chmod +x ./calculate_area.php
$ ./calculate_area.php 40
The square's surface area is 1600 units
Our Shapes\Square
class was successfully loaded without the need to explicitly include it.
Wrapping it up
In this tutorial we saw how Composer manages dependencies for PHP projects. This versatile tool is now part of many developers’ arsenal. I hope this article managed to bring you up to speed with Composer.
For a detailed overview of the capabilities of the command line interface, refer to the Composer documentation.