What's new in Guzzle 2.1

Written by unknown - - Aggregated on Tuesday January 24, 2012

There were some major improvements added to Guzzle in the last week. Guzzle is now more flexible, easy to use, and more powerful than ever. Here’s a list of the major features introduced in the 2.x series:

What is Guzzle?

Guzzle is a PHP HTTP client and framework for building RESTful web service clients. Guzzle allows you to truly reap the benefits of the HTTP/1.1 spec by providing managed persistent connections and the ability to easily send requests in parallel. In addition to taking the pain out of HTTP, Guzzle provides a lightweight framework for creating web service clients. With Guzzle’s built in error handling, OAuth support, and dynamically generated HTTP requests, building your next web service client on top of Guzzle will save you a ton of time.

Symfony2 EventDispatcher

The event system is the foundation of Guzzle’s flexibility. Using the Symfony2 EventDispatcher ensures that a well-tested and broadly adopted event framework powers the most critical aspect of Guzzle. Implementing the Symfony2 EventDispatcher helps to make the intent of event subscribers more explicit; instead of a single callback receiving all events dispatched from a subject, callbacks are registered individually for each event they subscribe to.

Listening to events

All classes in Guzzle that emit events implement the Guzzle\Common\HasDispatcherInterface. Any object that implements this interface has a getEventDispatcher() method to retrieve the EventDispatcher for that object.

In the following example, we are transparently retrying all 401 responses with an updated authorization token:

<?php

use Guzzle\Common\Event;

$client = new Guzzle\Http\Client('http://www.example.com/api/v1');

// Add custom error handling to any request created by this client
$client->getEventDispatcher()->addListener('request.error', function(Event $event) {

    if ($event['response']->getStatusCode() == 401) {

        $newRequest = clone $event['request'];
        $newRequest->setHeader('X-Auth-Header', MyApplication::getNewAuthToken());
        $newResponse = $newRequest->send();

        // Set the response object of the request without firing more events
        $event['response'] = $newResponse;

        // You can also change the response and fire the normal chain of
        // events by calling:
        // $event['request']->setResponse($newResponse);

        // Stop other events from firing when you override 401 responses
        $event->stopPropagation();
    }
});

$response = $client->get('restricted-resource.json')->send();
echo $response;

Building plugins

Guzzle ships with quite a few plugins out of the box: Over the wire logging, Caching forward proxy, Truncated exponential backoff, OAuth, Cookies, MD5 hash validation, Mock response queue, History, Basic authorization, Batch queue

If you need to extend Guzzle to support your web service, you can create a Symfony2 event subscriber. You’ll need to subscribe to specific events in the request cycle to extend Guzzle’s behavior.

Let’s say you’re building a client for a web service that requires a custom authorization header for every request. This fictional authorization plugin could be implemented like so:

<?php

namespace Guzzle\Foo;

use Guzzle\Common\Event;

class FooAuthPlugin implements Symfony\Component\EventDispatcher\EventSubscriberInterface
{
    private $secret;

    public function __construct($secret)
    {
        $this->secret = $secret;
    }

    public static function getSubscribedEvents()
    {
        return array('client.create_request' => 'onRequestCreate');
    }

    public function onRequestCreate(Event $event)
    {
        $request = $event['request'];

        $timestamp = time();
        $signature = hash_hmac('sha1', $request->getMethod() . '&'
            . rawurlencode($request->getResourceUri()) . '&' . $timestamp, $this->secret);

        $request->setHeader('Authorization', "FOO signature=\"{$signature}\", timestamp=\"{$timestamp}\"");
    }
}

Symfony2 Validator

Validating user data is a problem that has been solved by many other developers. Guzzle adopted the Symfony2 Validator component to leverage Symfony2’s robust validation system.

Guzzle uses the Symfony2 validator component when executing web service commands. Web service commands in Guzzle are basically collection of parameters that are turned into HTTP requests. You can enforce that a parameter is of a certain type before sending an HTTP request by utilizing a type attribute.

After generating a project skeleton and creating a client, you can start creating commands. The following example shows how you might create a command to create a new user.

<?php

namespace Guzzle\Foo\Command;

/**
 * Create a new user for the Foo web service
 *
 * @guzzle email      type="regex:/^[a-zA-Z0-9]{3,10}$/" required="true" doc="User email address"
 * @guzzle password   type="string" required="true" min_length="6" doc="Password"
 * @guzzle newsletter type="boolean" default="true" doc="Is the user subscribed to the newsletter?"
 */
class CreateUser extends Guzzle\Service\Command\AbstractCommand
{
    protected function build()
    {
        $this->request = $this->client->post('/users');
        $this->request->setHeader('Accept', 'application/json');
        $this->request->setBody(json_encode(array(
            'username' => $this->get('username'),
            'password' => $this->get('password'),
            'newsletter' => (bool) $this->get('newsletter')
        )), 'application/json');
    }
}

Guzzle uses DocBlock annotations to make creating commands easier. A number of default types are registered with the Guzzle\Service\Inspector to ensure that values passed into a command validate with the associated Symfony2 validator constraints. You can implement your own Symfony\Component\Validator\Constraint class to add custom validation to your web service client.

Persistent connections

Persistent HTTP connections are an extremely important aspect of the HTTP/1.1 protocol that is often overlooked by PHP HTTP clients. Persistent connections allows data to be transferred between a client and server without the need to reconnect each time a subsequent request is sent, providing a significant performance boost to applications that need to send many HTTP requests to the same host. Guzzle implicitly manages persistent connections for all requests.

All HTTP requests sent through Guzzle are sent using the same cURL multi handle. cURL will maintain a cache of persistent connections on a multi handle. As long as you do not override the default Guzzle\Http\Curl\CurlMulti object in your clients, you will benefit from application-wide persistent connections. More information about cURL’s internal design and persistent connection handling can be found at http://curl.haxx.se/dev/internals.html.

OAuth plugin

Guzzle now supports OAuth out of the box. Quit worrying about signing OAuth requests and start building your web service client with Guzzle!

<?php

$client = new Guzzle\Http\Client('http://api.twitter.com/1');
$oauth = new Guzzle\Http\Plugin\OauthPlugin(array(
    'consumer_key'    => 'my_key',
    'consumer_secret' => 'my_secret',
    'token'           => 'my_token',
    'token_secret'    => 'my_token_secret'
));
$client->getEventDispatcher()->addSubscriber($oauth);

$response = $client->get('statuses/public_timeline.json')->send();
var_export(json_decode((string) $response->getBody()));

Composer for dependency management

Guzzle has switched from git submodules to using Composer for dependency managment. You can add Guzzle to your composer enabled project by adding the following to your composer.json file:

{
    "require": {
        "guzzle/guzzle": "*"
    }
}

You then just call php composer.phar install, and you’re done! You can learn more about installing Guzzle using Composer, PHAR, PEAR, or GIT by reading the installation instructions.

Easier to dynamically generate HTTP requests

You can leverage Guzzle’s dynamically generated HTTP requests if the web service you are interacting with has a ton of very similar commands. All you need to do is create a Guzzle service description that describes the different commands supported by the web service.

You can implement the previously created CreateUser command using a JSON service description:

{
    "commands": {
        // Define an abstract command and extend it for each command
        "abstract": {
            // You would need to create this default command that would convert
            // all of the JSON parameters into a JSON entity body for requests
            "class": "Guzzle\Foo\JsonFooCommand",
            "params": {
                "headers": {
                    "Accept": "application/json"
                }
            }
        },
        "create_user": {
            // Extend the abstract command defined above
            "extends": "abstract",
            // Use a path relative to the base URL of the client
            "path": "users",
            "method": "POST",
            "params": {
                "username": {
                    "type": "regex:/^[a-zA-Z0-9]{3,10}$/",
                    "required": true,
                    "filter": "trim",
                    "location": "json"
                },
                "password": {
                    "type": "string",
                    "required": true,
                    "min_length": 6,
                    "location": "json"
                },
                "newsletter": {
                    "type": "boolean",
                    "default": true,
                    "location": "json"
                }
            }
        }
    }
}

The above JSON describes the commands that can be executed on the Foo web service. This JSON is roughly equivalent to the concrete command class we created earlier. I threw in a filter parameter that allows you to pass the input of the parameter through a series of functions that accepts a variable and returns the filtered variable. In the above example, we are running anything entered into the username parameter through the trim() function. You can specify multiple filters by separating them with a comma.

Assuming you saved the above JSON as foo.json, you can send validated HTTP requests to the web service by attaching a service description to your client:

<?php

use Guzzle\Service\Client;
use Guzzle\Service\Description\JsonDescriptionBuilder;

$description = JsonDescriptionBuilder::build('foo.json');
$client = new Client('http://foo.com/api/v1');
$client->setDescription($description);

$command = $client->getCommand('create_user', array(
    'username' => 'michael',
    'password' => 'foobar'
));

$jsonResult = $client->execute($command);
$request = $command->getRequest();
$response = $command->getResponse();

Going forward

The release of Guzzle 2.0 is a huge milestone for the project. As the project continues to mature, we hope to make it even easier to build web service clients. Have a suggestion on how we can make Guzzle better? Submit an issue to Guzzle on github.


« Chunked Transfer-Encoding in PHP with … - unknown

Dave Marshall - Test Data Builders »