Composer – All you need to know

What is composer?

Composer, created in 2012, is a dependency manager for PHP, it allows you to handle packages and their version to include in your project. You could compare it to Ruby Gem or Python pip or even NodeJS npm. The main repository of packages is Packagist, and you can find a lot of contribution from the whole PHP community. This should become you go to before you code anything 🙂

There was a previous attempt at package management with PEAR (PHP Extension and Application Repository) but honestly it was seen as a pain. Lot of outdated packages, no way to know how popular or followed was the package. Packaging itself was difficult and you couldn’t simply package your application as you can now (.phar files or git links).

One thing important to understand is that in PHP there are two main types of extensions: libraries and php modules. Libraries are the usual one you can find online or on Packagist, you can it in pure PHP and use it in your code.

Modules however are installed mainly through OS package and extend the capabilities of the PHP language with new primitives or treatment. It is generally downloaded as binary package (.so files) and come along with their own configuration (*.ini). Popular modules are for example php-intl for internationalization, or mb_string to deal with multi-bytes characters and strings.

In this article we will focus on libraries and dependency management with composer and how we deal with it the PHP way !

Installation

First let’s install composer, it is available on all platforms and you can find the installation procedure over here. On Mac and linux you usually have a package with your favorite package manager (yum, brew, pacman)

Now that you’re all set, let’s startup by doing a

composer init

An interactive shell will popup asking you several questions

Package name (<vendor>/<name>) : shenril/composer

The first one is your project naming, usually <vendor> is the name of the organization or individual and <name> is the actual name of the project.

Description []: Composer tutorial

Then a little optional description of your project

From here several optional fields will pop out , you can choose to skip it or fill in everything

<em>Author [, n to skip]: John Smith <</em><a href="https://web.archive.org/web/20200513105616/mailto:john@example.com"><em>john@example.com</em></a><em>></em>

<em>Minimum stability []: dev</em>

This is the minimum stability you accept from your dependencies. In order you can choose from this : dev, alpha, beta, RC, and stable. By default package will be filtered using this rule when importing them.

<em>Package Type (e.g. </em>library<em>, project, metapackage, composer-plugin) []:project</em>

This is an interesting setting that give you a glimpse of the possibilities of composer. You can choose your package type. By default we will make it a project but we’ll come back to this setting in advanced usage.

License[] : MIT

You can also insert your licensing model. As you’re free to publish your work on Packagist or elsewhere it is always interesting to add licensing information. Here by default we’ll go with the unrestricted MIT license.

Would you like to define your dependencies (require) interactively [yes]? no

<em>Would you like to define your </em>dev<em> dependencies (require-dev) interactively [yes]? no</em>

Last the setup prompt give you the possibility to add your dependencies interactively with a search system that will browse Packagist for you and write it in your composer.json file on the fly. For now we’ll say no.

Here you go ! You created your first composer.json file, let’s have a look of what is inside!

{
   "name": "shenril/composer",
   "description": "Composer tutorial",
   "type": "project",
   "license": "MIT",
   "authors": [
       {
           "name": "John Smith",
           "email": "john@example.com"
       }
   ],
   "minimum-stability": "dev",
   "require": {
}

As you would expect it is a json file which looks pretty simple and human readable !  We find all the fields we entered and for now the main part require is empty. That’s where all the magic will happen.

Require , as it states will contain the requirements for your project to run in production.

We also can add another section require-dev that will be the requirements to make it run in development environment (perfect place for lint and testing libraries).

Let’s give it a try and add some dependencies :

"require": {
       "php": ">=5.6.4",
       "symfony/symfony": "3.6.*"
   },
   "require-dev": {
       "phpunit/phpunit": "~5.0",
   }

Here we added our requirements, in production we enforce to have PHP superior to 5.6.4 to run and we added the symfony framework.

In the development requirements we included phpunit for unit testing of our project.

Regarding the version we choose here is a little explanation from composer.

Now that we have set up the requirements we can launch the install simply by doing

composer install

It will calculate all the dependencies and install them in a directory named /vendor/.

You will also notice that it creates a composer.lock. This file contains all the tree of dependencies with their specific version in JSON format. You can open it to see all the packages installed, their versions, the checksums associated with it and a lot of the magic composer uses to make life simpler for you. By default if you run composer install once again it will look at this file to install everything. This lock file is basically a frozen state of libraries that ensures that libraries won’t be updated in your back which ensure a real comfort and sense of security for you to use libraries safely.

Now that you know understand the basics, let’s move on to how you are going to use it in your day to day development.

Basic Usage

You now have a setup composer file, where you can add libraries to meet your will and the next step is actually to integrate it into your project.

Nothing simpler ! The autoloading of all the libraries imported is handled with a single line :

require_once __DIR__ . ‘/vendor/autoload.php’;

You can then add or remove packages using the command line or directly editing composer.json

<em>composer require</em> <em>***</em>

composer remove ***

The autoload file is created by composer install and will be in charge of all the class loading located in the vendor folders.

By default it is dynamic but if your dependencies don’t change much and you need for speed (production environment) you can dump static calls to libraries using the command :

composer dumpautoload

By default composer will install packages locally in your project vendor folder. However composer also allows you to install PHP packages globally onto your system with the global keyword:

composer global require “example:^1.0.0”

This will download the library in your home folder in a ~/.composer/ folder with again a composer.json, composer.lock and a vendor folder.

You can find this line in all the major frameworks! For instance in Laravel project you can go have a look at the file /boostrap/autoload.php which is in fired at each startup of a query.

From there you can use composer in your daily development by adding libraries and then refreshing your vendor directory with the command

composer update

You can also find useful command while you’re developing such as clearing the class caching and basically clear all cache related to class autoloading :

composer clearcache

To see all the possibilities offered by composer I strongly recommend you spend some time looking at its command list  and play a bit with it :

composer list

From there you can see you can do pretty much everything from the command line. From browsing packagist :

composer browse

Get information about a certain package:

composer info

From time to time I also recommend that you upgrade composer itself using its internal command:

composer self-update

Advanced capabilities

So far so good, we learnt the basic usage but let me tell you , Composer has a lot more to offer ! The best way to discover about it is to play with the composer.json file and look at its schema :

https://getcomposer.org/doc/04-schema.md

Types of project :

  • Project: is the most used type and is used for standalone projects.
  • Library: is the default choice, designed to provide libraries, utilities and any helper you would need
  • MetaPackage: an empty package that contains requirements and will trigger their installation, a good example would be a framework composed of a lot of libraries and dependencies.
  • Composer-plugin: as the name stands it allows you to write plugin to extend composer itself !! If you get the power of this, you know you can do almost anything. Famous example is for example Prestissimo to speed up installation through composer.

Autoloading

Another capability mostly unknown of composer is the possibility to add your own class loading that composer will take care for you.

Say you work with legacy code that you want to bootstrap and autoload, composer let you add your own path to the autoloading and it will take care of the loading for you !

"autoload": {
       "psr-4": {
           "Monolog\\": "src/",
           "Vendor\\Namespace\\": ""
       }
   }

This part may sound like a cheap trick but I can tell you it was a huge time savior for me, especially when migrating legacy codebase.

It lets you load PSR4 and PSR0 format which allows you to load the whole path or only one class to make it global :

"autoload": {
       "psr-0": { "UniqueGlobalClass": "" }
   }

And of course you’re going to ask me what happens if the code is so dirty that it does not follow any PSR ? Well Composer thought about that too and let you use the classmap and file loading too :

"autoload": {
       "classmap": ["src/", "lib/", "Something.php"]
   }

"autoload": {
       "files": ["src/MyLibrary/functions.php"]
   }

Once everything you need is loaded you can use it as soon as you declare the  require /vendor/autoload.php

It allows to safely replace/refactor classes and their interaction only by switching configuration into your composer.json ! And THAT is powerful !

Repositories

In addition to let you install packages, load your own classes, composer also allows you to add your own repositories in parallel with packagist.org!

Once you add your repository, you will be able to install your own package by adding them as any other to the require section of the composer.json file.

There are two main ways to add your own repository :

"repositories": [
       {
           "type": "vcs",
           "url": "https://github.com/Seldaek/monolog"
       },
]

Git repository

Basically you simply add your git repository as VCS type into the repository. The main downside is that you have to add packages one by one and therefore the repository lose its main advantage to my sense. It can still deals with versioning you can use in the require section but for me the second solution is always the preferred one.

Composer Repository

In this case the objective is to create a repository such as packagist.org that will serve a lot of different packages. You can administer it, add packages, make it public or private as you would prefer and handle all the versioning.

To install this composer repository I recommend using Satis that lets you create a simple static repository. You can couple it with a web interface to make easier such as satis-go or satisfy.

In my case after some tuning it looks like this :

Once you have created this repository, ideally you’d give it a DNS address and simply add it into your composer.json file

"repositories": [
       {
           "type": "composer",
           "url": "http://packages.example.com"
       },<br> ]

Once done, as always you just need to add the packages you need into the require section to add them to your project dependencies. Composer will handle the versioning, connection and so on for you 🙂 Isn’t life wonderful ?

Scripts

Last feature I would like to introduce is the script section. This part let you trigger action on specific event of composer. The event handled are the following:

  • pre-install-cmd: occurs before the install command is executed with a lock file present.
  • post-install-cmd: occurs after the install command has been executed with a lock file present.
  • pre-update-cmd: occurs before the update command is executed, or before the install command is executed without a lock file present.
  • post-update-cmd: occurs after the update command has been executed, or after the installcommand has been executed without a lock file present.
  • post-status-cmd: occurs after the status command has been executed.
  • pre-archive-cmd: occurs before the archive command is executed.
  • post-archive-cmd: occurs after the archive command has been executed.
  • pre-autoload-dump: occurs before the autoloader is dumped, either during install/update, or via the dump-autoload command.
  • post-autoload-dump: occurs after the autoloader has been dumped, either during install/update, or via the dump-autoload command.
  • post-root-package-install: occurs after the root package has been installed, during the create-project command.
  • post-create-project-cmd: occurs after the create-project command has been executed.
  • pre-dependencies-solving: occurs before the dependencies are resolved.
  • post-dependencies-solving: occurs after the dependencies have been resolved.
  • pre-package-install: occurs before a package is installed.
  • post-package-install: occurs after a package has been installed.
  • pre-package-update: occurs before a package is updated.
  • post-package-update: occurs after a package has been updated.
  • pre-package-uninstall: occurs before a package is uninstalled.
  • post-package-uninstall: occurs after a package has been uninstalled

It means that for any of these events you can launch a set of command of your choice to give you great freedom to extend or automate all of your processes ! The triggers are added as follows :

"scripts": {
       "post-update-cmd": "MyVendor\\MyClass::postUpdate",
       "post-package-install": [
           "MyVendor\\MyClass::postPackageInstall"
       ],
       "post-install-cmd": [
           "MyVendor\\MyClass::warmCache",
           "phpunit -c app/"
       ],
}

You can call your php classes, you can call CLI commands and so on. And as you can see, the commands are a array to you can chain them to the desired effect !

For now the weak point is the error handling on the output of these command but come on, composer can’t do all your job, at least not yet 😉

About Composer 2

The 24th of October 2020, Packagist released the new major version of Composer, here is the news about it.

It was a major rewrite but the main gain was definitely performance. All the functions presented are still valid and backwards compatibility.

However we can note some interesting improvements in this version:

  • At least 50% speed and memory usage improvements
  • Better processing of dependencies trees leading to fewer conflicts
  • Error reporting is much better
  • Partial package update: for example the ability to upgrade only one of your package independently

I definitely recommend upgrading to this version, even though some plugins you had may fail, there is so much gain by switching to this new version. I was genuinely surprised by the speed to update my projects !

That’s all folks ! I hope this could help you to have better understand of composer and all it can do for you, and hopefully you’ll look with interest into these advanced capabilities to get the best of it !

Don’t hesitate to comment or add your tips on composer !

You may also like...