Blog

The Digital Agency for International Development

PHP dependency management using Composer

By Chris Wilson on 14 March 2013

Anyone who has written or worked with unit tests in PHP should know about PHPUnit. We've been using it in several projects for many years.

Unfortunately PHPUnit is split into many modules, which makes it difficult to install manually. It's possible to use PEAR or operating system packages to handle the dependencies for you, but this leads to other problems:

  • Each developer must set up the development environment themselves, making it harder to get started working on a project.
  • It's easy to install the wrong versions of packages, resulting in expected test failures.
  • The PHPUnit package in Ubuntu 12.04 LTS is broken and unusable.

I want to keep all the dependencies of the project inside the project, to make it easier for developers to pick up and work on, like virtualenv in Python. Unfortunately PEAR makes this basically impossible, because it hard-codes the complete directory paths of installed packages in a binary file, which stops you from moving the code to a different directory or computer. There are many other problems with PEAR.

Then I wanted to fork and modify part of PHPUnit (which turned out to be a mistake), so I decided to try installing PHPUnit and its dependencies as PHP Submodules. This has the advantage of locking each project to a specific version (commit) and I'd only have to manage the dependencies once. Other developers would automatically get the right dependencies installed.

Unfortunately the PHPUnit author used a non-standard directory layout in some of his projects and refused to fix it. So I had to fork this project just to move the files around so that the other dependencies could find them.

Recently a few events combined to make me search for alternatives to this strategy:

  • PHPUnit's transitive dependencies ballooned again.
  • More and more of them required the same forking just to move files around.
  • I'd abandoned all hope of customising PHPUnit itself, and implemented the functionality I wanted in my test class instead.
  • It seemed impossible to find a version of Symfony/Component/Yaml that actually has an autoloader.php in Github.

It seems that PHPUnit's preferred approach to solving the dependency and installation issues is a new project, Composer. I decided that I was fighting the rising tide, and I should try out using Composer instead of Git submodules. There have been many issues with Composer, but it did actually work for me.

Installing PHPUnit through Composer turned out to be quite easy:

  • Install Composer into a directory of my choice:

    curl -sS https://getcomposer.org/installer | php -- --install-dir=web/lib
    
  • Create a very simple web/lib/composer.json file, listing the PHPUnit version and the relative directory where I wanted to install it:

    {
        "require": {
            "phpunit/phpunit": "3.7.*"
        },
        "config": {
            "bin-dir": "."
        }
    }
    
  • Run composer to download and install all the dependencies:

    php composer.phar install
    

And that was it. It also turned out to be much easier to invoke PHPUnit from inside the CodeIgniter test harness that I'm developing when it's installed this way. It's just one line now:

require(__DIR__."/../web/lib/phpunit");

And I no longer have to add each dependency individually to the PHP include path, because I think Composer locates and loads the modules for me automatically.

I think it's worth using Composer instead of PEAR in your PHP projects, if all of your dependencies support it, and encourage those that don't yet support Composer to start working on it.