Drupal 8 Now: Composer dependencies in Drupal 7

Share:

One of the key goals of namespaces in PHP is to avoid collisions between class and function names between different libraries. Using namespaces and the PSR-0/4 standard creates a clean and simple way of sharing code across projects. This is a core part of the Drupal 8 architecture.

This approach has been a large factor in the PHP Renaissance and the huge amount of sharing of code across PHP external libraries.

In this post, I will show how we can leverage external libraries in existing Drupal 7 sites, using similar techniques to how it is being done in Drupal 8.

In Drupal 7, The standard way to load in 3rd-party libraries is to put them in the sites/all/libraries folder, install the Libraries module, and then implement hook_libraries_info() to tell the Libraries module about your external library.

In Drupal 8, most of the code is in PSR-0/4 namespaced classes. External libraries are also written in PSR-0/4 classes and are brought in using Composer. They are passed into Drupal classes using Dependency Injection and managed with the Symfony2 Service Container. This is a much cleaner and simpler way of using external libraries.

The good news is, you can take much the same approach with your custom Drupal 7 modules calling external libraries, right now.

Setting Up

There are a few moving parts in our example module. Lets have a quick overview of them first.

HTTPBin

For our example module, we want to grab some data from an external web service, such as httpbin.org. This is just a test web service which returns information about the requestion you made. In our case we will just request the IP address of the incoming request we made from a JSON response. Any other web serice will do.

Once we have it, we are just going to display it in a block. Simple!

Guzzle

Guzzle is a http client that is replacing drupal_http_request() in Drupal 8. It's an object-oriented, extensible and fully featured client, with a very simple API. Perfect for interacting with web services!

Composer

Composer is a major breakthrough for the open source PHP community. Along with PSR-0 and Packagist, It has provided a standardised way of sharing code among different projects. You just need to create a composer.json file, declare your dependencies, and then download them with the command line tools.

XAutoload

As mentioned in previous posts, the excellent XAutoload makes it easy to simply call the classes you need, without manually loading them, with a simple API.

Putting it all together

Step 1: Install composer

If you haven't got composer installed yet, the installation instructions are a very simple one-liner:

We're using Mac OSX, so we want to put composer into /usr/local/bin so its automatically added to our path. We also specify a specific version, to avoid the "works on my machine" problem.

More detailed installation instructions are available on the Composer site.

Step 2: Create a composer.json file

In order to load in the Guzzle library, we need to specify it as a dependency. Create the following composer.json file in the root directory of our project.

{
"require": {
"guzzlehttp/guzzle": "~4.0.2"
}
}

Step 3: Install the libraries with composer

Assuming you have composer installed, run the following:

composer install

Composer will generate a lock file composer.lock and download dependencies into <project_root>/vendor. If we wanted to put them somewhere else (e.g. sites/all/vendor) we can use the COMPOSER_VENDOR_DIR environment variable.

Step 4: Tell XAutoload where to load our composer autoload files

We need to let XAutoload know where composer has installed its vendor directory, and autoload files. Fortunately, doing this in our guzzle_example.module is a single line function.

Here were are simply creating a new Guzzle Client object, and calling the get method with the url to our web service. Look mum! No require()! The web service returns a JSON response, with a key origin that has our IP address. Try it yourself! http://httpbin.org/get

Step 6: Output the data in a block

From here, we could wire can wire this up to some standard Drupal 7 custom block code, to output this a the site.

Comments

This example is for your own custom modules using external libraries, so the assumption is you have a single composer.json for your project, which manages all dependencies. This is something that we currently do.

Managing dependencies for an unknown number of contrib modules is a a difficult problem, and one that hasn't really been solved yet! One solution is to try to naively merge them from multiple composer.json files like Composer Manager does.

Imo, composer manager has the avantage of supporting composer dependencies in contrib modules also. More and more recent modules does have a composer.json file.
So Composer resolving contrib+custom package dependecies is a big win for me, but having some room for improvements.

Confused by Step 5 when you say, "Composer Manager does all the hard work of auto-loading our classes..."

This is the first time "Composer Manager" is mentioned. Are you referring to the Drupal.org Composer Manager module? And I thought Xautoload was supposed to handle autoloading? Are you using both Composer Manager AND Xautoload?

We previously had been using Composer Manager, but found creating a single composer.json file that we manually manage ourselves, was preferable to the way Composer Manager naively merged the composer.json files together. Either approach will work, it just depends on your level of confidence with composer.