1

Sending Arbitrary Data from Request to Response in Guzzle 6

Posted in PHP

Say you’re pooling requests in Guzzle and want the responses to have access to data such as the request URL. Guzzle doesn’t currently allow for this but that’s all fixed with the help of a simple middleware class.

 

Usage

When requesting a URL with GuzzleHttp\Client, pass a RESPONSE_META array in your requests second argument like so:

1
2
3
4
5
6
$client->get($url, [
    'RESPONSE_META' => [
        'url' => $url,
        'some_data' => 'foo',
    ],
]);

The data is attached as a header to the response. Access it like so: (Remembering to prepend X-GUZZLE-META- to your key)

1
$some_data = $response->getHeaderLine('X-GUZZLE-META-some_data');

 

Complete Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$names = ['Alpha', 'Beta', 'Charlie', 'Delta'];
$stack = GuzzleHttp\HandlerStack::create();
$stack->push(GuzzleResponseMetaMiddleware::middleware());$client = new GuzzleHttp\Client([
    'handler' => $stack]);
$requests = function() use ($client, $names) {
    foreach ( $names as $name )
    {
        $url = "Http://mydomain.com/?name=".$name;
 
        yield function() use ($client, $url, $name) {
            return $client->getAsync($url, [
                'RESPONSE_META' => [                    'url' => $url,                    'name' => $name,                ],            ]);
        };
    }
};
$pool = new GuzzleHttp\Pool($client, $requests(), [
    'concurrency' => 5,
    'fulfilled' => function (Psr\Http\Message\ResponseInterface $response, $index) {
        $url = $response->getHeaderLine('X-GUZZLE-META-url');        $name = $response->getHeaderLine('X-GUZZLE-META-name'); 
        // do something with this info
    },
]);
 
// Initiate the transfers and create a promise
$promise = $pool->promise();
 
// Force the pool of requests to complete.
$promise->wait();

 

The Middleware Class

Here is the middleware class I used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?php
 
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
 
 
/**
 * Class GuzzleEffectiveUrlMiddleware
 * @package Mypolice\Crimemap
 *
 * Taken from https://stackoverflow.com/a/31962446
 * Saves the request URL and custom meta for use in a Guzzle response.
 *
 * Usage:
 * <?php
 * use GuzzleHttp\Client;
 * use Thinkscape\Guzzle\EffectiveUrlMiddleware;
 *
 * // Add the middleware to stack and create guzzle client
 * $stack = HandlerStack::create();
 * $stack->push(EffectiveUrlMiddleware::middleware());
 * $client = new Client(['handler' => $stack]);
 *
 * // Test it out!
 * $response = $client->get('http://bit.ly/1N2DZdP', [
 *      'RESPONSE_META' => [
 *          'some_key' => 'some_value',
 *      ]
 * ]);
 * echo $response->getHeaderLine('X-GUZZLE-EFFECTIVE-URL');
 * echo $response->getHeaderLine('X-GUZZLE-META-some_key');
 *
 */
class GuzzleResponseMetaMiddleware
{
    /**
     * @var Callable
     */
    protected $nextHandler;
    /**
     * @var string
     */
    protected $headerName;
    /**
     * @param callable $nextHandler
     * @param string   $headerName  The header name to use for storing effective url
     */
    public function __construct(
        callable $nextHandler,
        $headerName = 'X-GUZZLE-EFFECTIVE-URL'
    ) {
        $this->nextHandler = $nextHandler;
        $this->headerName = $headerName;
    }
    /**
     * Inject effective-url header into response.
     *
     * @param RequestInterface $request
     * @param array            $options
     *
     * @return RequestInterface
     */
    public function __invoke(RequestInterface $request, array $options)
    {
        $fn = $this->nextHandler;
        return $fn($request, $options)->then(function (ResponseInterface $response) use ($request, $options) {
            $response = $response->withAddedHeader($this->headerName, $request->getUri()->__toString());
 
            if ( isset($options['RESPONSE_META']) && is_array($options['RESPONSE_META']) )
                foreach ( $options['RESPONSE_META'] as $key => $value )
                    $response = $response->withAddedHeader('X-GUZZLE-META-'.$key, $value);
 
            return $response;
        });
    }
    /**
     * Prepare a middleware closure to be used with HandlerStack
     *
     * @param string $headerName The header name to use for storing effective url
     *
     * @return \Closure
     */
    public static function middleware($headerName = 'X-GUZZLE-EFFECTIVE-URL')
    {
        return function (callable $handler) use (&$headerName) {
            return new static($handler, $headerName);
        };
    }
}