Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guzzle 6: no more json() method for responses

Tags:

php

guzzle

Previously in Guzzle 5.3:

$response = $client->get('http://httpbin.org/get');
$array = $response->json(); // Yoohoo
var_dump($array[0]['origin']);

I could easily get a PHP array from a JSON response. Now In Guzzle 6, I don't know how to do. There seems to be no json() method anymore. I (quickly) read the doc from the latest version and don't found anything about JSON responses. I think I missed something, maybe there is a new concept that I don't understand (or maybe I did not read correctly).

Is this (below) new way the only way?

$response = $client->get('http://httpbin.org/get');
$array = json_decode($response->getBody()->getContents(), true); // :'(
var_dump($array[0]['origin']);

Or is there an helper or something like that?

like image 462
rap-2-h Avatar asked May 29 '15 12:05

rap-2-h


6 Answers

I use json_decode($response->getBody()) now instead of $response->json().

I suspect this might be a casualty of PSR-7 compliance.

like image 169
meriial Avatar answered Oct 04 '22 17:10

meriial


You switch to:

json_decode($response->getBody(), true)

Instead of the other comment if you want it to work exactly as before in order to get arrays instead of objects.

like image 35
dmyers Avatar answered Oct 04 '22 18:10

dmyers


I use $response->getBody()->getContents() to get JSON from response. Guzzle version 6.3.0.

like image 41
jusep Avatar answered Oct 04 '22 17:10

jusep


If you guys still interested, here is my workaround based on Guzzle middleware feature:

  1. Create JsonAwaraResponse that will decode JSON response by Content-Type HTTP header, if not - it will act as standard Guzzle Response:

    <?php
    
    namespace GuzzleHttp\Psr7;
    
    
    class JsonAwareResponse extends Response
    {
        /**
         * Cache for performance
         * @var array
         */
        private $json;
    
        public function getBody()
        {
            if ($this->json) {
                return $this->json;
            }
            // get parent Body stream
            $body = parent::getBody();
    
            // if JSON HTTP header detected - then decode
            if (false !== strpos($this->getHeaderLine('Content-Type'), 'application/json')) {
                return $this->json = \json_decode($body, true);
            }
            return $body;
        }
    }
    
  2. Create Middleware which going to replace Guzzle PSR-7 responses with above Response implementation:

    <?php
    
    $client = new \GuzzleHttp\Client();
    
    /** @var HandlerStack $handler */
    $handler = $client->getConfig('handler');
    $handler->push(\GuzzleHttp\Middleware::mapResponse(function (\Psr\Http\Message\ResponseInterface $response) {
        return new \GuzzleHttp\Psr7\JsonAwareResponse(
            $response->getStatusCode(),
            $response->getHeaders(),
            $response->getBody(),
            $response->getProtocolVersion(),
            $response->getReasonPhrase()
        );
    }), 'json_decode_middleware');
    

After this to retrieve JSON as PHP native array use Guzzle as always:

$jsonArray = $client->get('http://httpbin.org/headers')->getBody();

Tested with guzzlehttp/guzzle 6.3.3

like image 36
andrew Avatar answered Oct 04 '22 17:10

andrew


$response is instance of PSR-7 ResponseInterface. For more details see https://www.php-fig.org/psr/psr-7/#3-interfaces

getBody() returns StreamInterface:

/**
 * Gets the body of the message.
 *
 * @return StreamInterface Returns the body as a stream.
 */
public function getBody();

StreamInterface implements __toString() which does

Reads all data from the stream into a string, from the beginning to end.

Therefore, to read body as string, you have to cast it to string:

$stringBody = (string) $response->getBody()


Gotchas

  1. json_decode($response->getBody() is not the best solution as it magically casts stream into string for you. json_decode() requires string as 1st argument.
  2. Don't use $response->getBody()->getContents() unless you know what you're doing. If you read documentation for getContents(), it says: Returns the remaining contents in a string. Therefore, calling getContents() reads the rest of the stream and calling it again returns nothing because stream is already at the end. You'd have to rewind the stream between those calls.
like image 42
simPod Avatar answered Oct 04 '22 19:10

simPod


Adding ->getContents() doesn't return jSON response, instead it returns as text.

You can simply use json_decode

like image 28
Moh Avatar answered Oct 04 '22 19:10

Moh