This post has already been read 3933 times!

Fotolia_45264356

PHP Dependency Management with Composer

In this article I will introduce you to another great project, Composer. Maybe you’ve experienced the pain of working on a PHP application which uses third-party libraries and then trying to keep them and their dependencies up to date. If so, Composer can soothe your pain.

Composer gets you the libraries you want at the versions you need. And if those libraries use other libraries, it can install those and manage them as well. Dependency management can be a hassle-free experience using Composer.

Installing Composer

Composer is bundled as an executable Phar archive, so make sure you have the Phar extension enabled in your php.ini file (uncomment extension=phar.so).

I recommend download the latest snapshot of the Composer executable directly from the project’s website.

Alternatively, there is an installer script that you can run. If you’re comfortable with the issues surrounding such installers, you can cut and paste the following taken from the Composer website:

curl -s https://getcomposer.org/installer | php

To make Composer globally accessible on your system, move the resulting composer.phar file to a suitable location, like so:

sudo mv composer.phar /usr/local/bin/composer

Using Composer

If you’ve done any Ruby or Node.js programming then Composer may seem a bit familiar to you. The dependency manager was inspired by Bundler and npm. You first create a composer.json file that lists all of your project’s dependencies, and then with a simple command you can fetch or maintain them.

To add a library to your project, create a file named composer.json with content that resembles this example:

{ 
    "require": { 
        "illuminate/foundation": "1.0.*"
    },
    "minimum-stability": "dev"
}

The require key lists the project’s dependencies. The dependency in this example is Illuminate (version 4 of the Laravel framework). Of course Illuminate depends on a whole lot of other packages, and Composer will install these too.

Following the package name you see the required version number. I’ve specified the application can use any minor update in the 1.0 branch. You can also specify specific versions or versions within a given range. You can find more information on package versions on the Composer website.

The minimum-stability key is present because not all of Illuminate’s dependencies are stable yet. If omitted, the rule’s default value is “stable” and the install would fail.

Now to actually install Illuminate, run the following in your project’s directory:

php composer.phar install

Composer creates the folder named vendors and downloads the dependencies into it. As a convenience, Composer also creates a PSR-0 autoloader for you to pull the libraries into your code; simple require vendors/autoloader.php in your code to use them.

require_once "vendors/autoloader.php";
// your code here

All of Composer’s data on installed libraries in the file composer.lock. Composer tracks versions of libraries are currently installed and what their VCS URL is. It’s like a registry with all information about the local libraries in it. When installing/updating libraries, Composer also updates this file.

You can then keep the packages up-to-date by running composer.phar update.

Packaging Your Own Code

You might be thinking, how does Composer know where to download the code just by the name “illuminate/foundation”? Composer has an official repository named Packagist that it connects to. You can search there to see what libraries are available for management through Composer. You can even create your own packages and submit them to Packagist making them available to others.

Creating your own libraries is quite simple since your project can be viewed as a library with it’s dependencies already listed in composer.json. In fact, the documentation says: “the only difference between your project and libraries is that your project is a package without a name.”

In order to package your code for others to use, you need to define some additional keys:

{ 
    "name": "AlexCogn/Illumination",
    "version" : "1.0.0",
    "require": { 
        "illuminate/foundation": "1.0.*"
    },
    "minimum-stability": "dev"
}

If you have your project on GitHub, it is recommended to use your account name in the project’s namespace. If you are indeed using GitHub, Packagist can fetch the version numbers from there so you don’t really have to define it explicitly as I did above.

Now you can publish the VCS link of your project to Packagist. Pagckagist makes this really easy; either register a new account or log in with GitHub and then click the giant Submit Package button. Provide the repository’s URL and Packagist will crawl it.

Composer Scripts

Any build automation tool worth its salt must provide the ability to script a range of automated tasks – from building, packaging, and running test suites, to deployment on staging and production systems. Phing, for example, is based on Ant and permits you to define such tasks in XML build files.

Composer differs in this regard in that it makes no assumptions as to what these tasks are, or if they are to be performed at all. What Composer does instead is expose its pre- and post- install/update/uninstall event hooks during execution which you can callback using “scripts”, much the same way that Pyrus provides the ability to define custom commands in the package.xml via the --plugin option to its install, upgrade, and uninstall commands.

The scripts property is defined in the root JSON object of your root package’s composer.json file. You can define any number of PHP static methods (which must be autoloadable by Composer’s autoloader mechanism), command-line executables, or a combination of both.

Any custom code or package-specific commands defined by these scripts are then called during Composer’s execution process. The caveat is that only the scripts defined in the root package’s composer.json are executed. Composer will not execute any scripts specified in a dependency of the root package.

The following events are fired during the Composer execution process:

  • pre-install-cmd – occurs before the install command is executed
  • post-install-cmd – occurs after the install command is executed
  • pre-update-cmd – occurs before the update command is executed
  • post-update-cmd – occurs after the update command is executed
  • pre-package-install – occurs before a package is installed
  • post-package-install – occurs after a package is installed
  • pre-package-update – occurs before a package is updated
  • post-package-update – occurs after a package is updated
  • pre-package-uninstall – occurs before a package is uninstalled
  • post-package-uninstall – occurs after a package is uninstalled

These are fairly self-explanatory, and I think you will agree, that beauty lies in its simplicity. But to illustrate, here’s an example root package composer.json:

{
    "name": "MyProject",
    "description": "An example to demonstrate the use of Composer scripts",
    "version": "1.0.0",
    "require": {
        "php": ">=5.3",
        "ext-xsl": "*",
        "ext-imap": "*",
        "ext-gd": "*"
      },

    "autoload": {
        "psr-0": {
            "MyProject": "src/"
        }
    },

    "scripts": {
        "pre-install-cmd": "MyProject\\Installer::preInstall",
        "post-install-cmd": [
            "MyProject\\Installer::postInstall"
        ],
        "post-package-install": [
            "MyProject\\Installer::postPackageInstall",
            "phpunit -c /tests",
            "./bin/install.sh"
        ]
    }
}

I’ve defined some scripts for the pre-install-cmd, post-install-cmd, and post-package-install events. As you can see, we can define any combination of static PHP methods and command-line executables. In the case of the post-package-install event, it also executes some unit tests and a custom install script.

Here’s what our example script looks like:

<?php namespace MyProject; use Composer\Script\Event; class Installer {     public static function preInstall(Event $event) {         // provides access to the current Composer\IO\ConsoleIO         // stream for terminal input/output         $io = $event->getIO();
        if ($io->askConfirmation("Are you sure you want to proceed? ", false)) {
            // ok, continue on to composer install
            return true;
        }
        // exit composer and terminate installation process
        exit;
    }

    public static function postInstall(Event $event) {
        // provides access to the current Composer instance
        $composer = $event->getComposer();
        // run any post install tasks here
    }

    public static function postPackageInstall(Event $event) {
        $installedPackage = $event->getComposer()->getPackage();
        // any tasks to run after the package is installed?
    }
}

When each of these events are fired, Composer’s internal event handler passes a Composer\Script\Event object as the first (and only) argument to each of the callbacks. The Event object exposes the following getters for other Composer objects to your callback:

  • getComposer() – returns the current instance of Composer\Composer
  • getName() – returns the name of the event being fired
  • getIO() – returns the current input/output stream which implements Composer\IO\IOInterface for reading/writing to the console

You can refer to the Composer API documentation for each of the method signatures and for the other methods that each of these objects expose – in particular, the Composer instance and the IO interface.

While this seemingly rudimentary implementation may not appear as “powerful” as Phing definitions, its simplicity belies its incredible flexibility. It leverages your existing knowledge investment in PHP, and with a little creativity and imagination you can use Composer’s dependency resolver and native PHP scripting to create some fairly complex build and take-down tasks. You could even integrate this into Jenkins for continuous integration.
Summary

In this article, I’ve introduced a rudimentary example of how Composer scripts can be used to perform build automation. These tasks can be as simple or as complex as you require, since they leverage your existing knowledge investment in PHP. And hopefully, this article will inspire you to use Composer for more than just dependency management. For more information on how to use Composer scripts, see getcomposer.org/doc/articles/scripts.md.

Comments are closed.

Post Navigation