Guzzle 5 and RingPHP

Guzzle 5 has been officially released. Guzzle 5 now utilizes RingPHP as the transport layer, making it significantly easier to bind Guzzle to existing networking libraries (e.g., sockets, event loop libraries, etc.). Guzzle 5 can now send both synchronous and asynchronous requests using a consistent interface.

With RingPHP, Guzzle does not require cURL and can be used with any HTTP transport mechanism. I’d love to help anyone who is interested in creating RingPHP adapters to bind Guzzle to another library. For example, WyriHaximus on Github is working on binding Guzzle to ReactPHP. (In fact, Guzzle 4 did not require cURL, though it was much harder to use an alternate transport.)

RingPHP

RingPHP is a new PHP library that abstracts away the HTTP protocol into simple hashes and PHP functions. RingPHP handlers are implemented using PHP callables that accept an associative array of request data and return a future array of response data that can be used synchronously as an array or asynchronously using a promise. While Guzzle only uses RingPHP as a HTTP client, it can also be used to power HTTP servers.

The use of RingPHP now allows Guzzle to send HTTP redirects and retries concurrently (Guzzle previously performed blocking redirects and retries).

Promises and Futures

Guzzle 5 now utilizes future values to represent asynchronous response objects. These response objects (GuzzleHttp\Message\FutureResponse) can be used as a normal response to use them synchronously, or utilized asynchronously using the promise() method of the response.

<?php
$response = $client->get('http://httpbin.org', ['future' => true]);

// Use the response asynchronously
$response->then(function ($response) {
    echo $response->getStatusCode(); // 200
});

// Use the response synchronously
echo $response->getStatusCode(); // 200

// Explicitly block on the response to be ready
$actualResponse = $response->wait();

PSR-7 Compatibility

As of today, Guzzle 5 is now completely compatible with the current draft of the PSR-7 message proposal (which didn’t require many changes considering PSR-7 is very similar to Guzzle’s existing interfaces). This includes the newly released guzzlehttp/streams 3.0.

Iterable and Callable Streams

Guzzle streams can now be created for PHP iterators and callables, making it much easier to generate streams of data on the fly using PHP generators.

<?php
$gen = function ($size) {
    while ($size > 0) {
        yield '.';
        $size--;
    }
};

$stream = GuzzleHttp\Stream\Stream::factory($gen(1000));

echo $stream->read(5);
// Outputs: .....

New Events

The event system in Guzzle got several upgrades.

  1. There is a new “progress” event that can be used to get the progress of HTTP uploads and downloads.
  2. There is a new “end” event that is a terminal event that is triggered once per HTTP request. The “end” event is triggered for both successful and failed requests.
  3. There is now a retry() function on GuzzleHttp\Event\ErrorEvent and GuzzleHttp\Event\CompleteEvent that allows you to retry a request without emitting multiple “end” events. The retry() method accepts an optional delay argument that specifies how long to wait before retrying. When using a non-blocking adapter, this delay can happen concurrently while other requests are transferred.

In addition to using promises for asynchronous response handling, Guzzle’s signal slot event system can also be used for handling asynchronous responses and makes it easy to create sharable event subscribers that can be used across projects (like the log subscriber, retry subscriber, etc.).

<?php
use GuzzleHttp\Event\EndEvent;

$request = $client->createRequest('GET', 'http://httpbin.org/get');
$request->getEmitter()->on('end', function (EndEvent $e) {
    if ($e->getException()) {
        echo 'Got an error! ' . $e->getException()->getMessage();
    } else {
       echo 'Got a response: ' . $e->getResponse()->getStatusCode();
    }
});

$client->send($request);

Signal slot events should be used when creating event subscribers that modify the request or response before it is returned. These signal slot events will complete before any promises are resolved. Other than the “end” event, signal slot events can be triggered multiple times for a single request transfer, whereas a promise is resolved or rejected only once per transfer.

Upgrading to Guzzle 5

Upgrading your application to Guzzle 5 should be relatively painless and won’t require changes for most users. Please see the changelog and upgrading guide for more information.