Drupal 8 Now: Sử dụng Composer dependencies trong Drupal 7

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:

curl -sS https://getcomposer.org/installer | php -- --version=1.0.0-alpha8 --install-dir=/usr/local/bin --filename=composer
 

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.

/**
 * Implements hook_autoload().
 */
function guzzle_example_xautoload($api) {
  $api->absolute()->composerDir('vendor/composer');
}

Step 5: Use Guzzle in your module

XAutoload does all the hard work of auto-loading our classes, so we can just instantiate them directly in our code:

<?php
function guzzle_example_get_host_ip() {
  $client = new \GuzzleHttp\Client();
  $response = $client->get('http://httpbin.org/get');
  $json = $response->json();
  return $json['origin'];
}

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.

<?php
function guzzle_example_block_info() {
  return array(
    'host_ip' => array(
      'info' => t("What's My IP?"),
      'cache' => DRUPAL_NO_CACHE,
    ),
  );
}

function guzzle_example_block_view($delta = '') {
  $block = array();
  switch ($delta) {
    case 'host_ip':
      $block['subject'] = t("What's My IP?");
      $block['content'] = guzzle_example_get_host_ip();
      break;
  }
  return $block;
}

Conclusion

A lot of PHP libraries are now being written using the PSR-0/4 standards, and its easier than ever to use them in your custom module code.

Why wait for Drupal 8?