203

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?

rap-2-h
  • 30,204
  • 37
  • 167
  • 263

6 Answers6

356

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

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

meriial
  • 4,946
  • 2
  • 23
  • 21
  • 5
    Nothing in the documentation that makes this explicit but it does appear they've phased out the `$response->json()` helper. – paperclip Jun 08 '15 at 15:35
  • 69
    If you're expecting an array response like how the original `->json()` worked, use `json_decode($response->getBody(), true)` instead to get an array instead of a stdObject – Jay El-Kaake Dec 23 '15 at 04:53
  • 21
    Using `strict_types`, I needed to cast the Guzzle response body to string before decoding it: `json_decode((string) $response->getBody(), true)` – Yoan Tournade Mar 23 '18 at 12:15
  • 2
    I always liked to use `\GuzzleHttp\json_decode` (or `\GuzzleHttp\Utils::jsonDecode` depending on the Guzzle version you're on) which has a compatible signature to `\json_decode`, but throws an exception if there's an error, leveraging proper err handling. – Adrian Föder Jul 18 '20 at 10:02
  • 1
    Under `strict_types`, you can cast to a `string` as @YoanTournade suggested or instead use `$response->getBody()->getContents()`, which returns a string. – The Unknown Dev Nov 09 '20 at 14:33
  • 1
    @AdrianFöder You can do this quite easily with `json_decode($json, $assoc, $depth, JSON_THROW_ON_ERROR)`, but you'll have to wrap the function yourself if you still want that convenience. – Steen Schütt Dec 29 '21 at 15:22
128

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.

dmyers
  • 1,413
  • 1
  • 10
  • 6
36

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

jusep
  • 469
  • 4
  • 5
  • 15
    Calling `getContents()` in the response body will empty the stream and the next call to `getContents()` will return empty. If you want to get the body as string use: `strval($response->getBody())` – JVitela Aug 01 '18 at 15:47
  • 4
    I wish this comment was higher. I was logging my response using getContent and when I went to parse a line later, my array was empty. Cost me hours. Thanks! – Colin Jun 13 '19 at 18:31
20

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

andrew
  • 409
  • 4
  • 7
  • This is some good stuff. Using in a Rest API Client task that i just picked up at work. I do have one question about your answer though. Was your JsonAwareResponse class intended to be under the GuzzleHttp namespace? I ended up just creating that class under my own namespace, but for a second I was searching around GuzzleHttp's codebase looking for that class. :) Thanks again! – sdaugherty Aug 19 '19 at 16:40
  • 1
    Don't use this solution because it breaks PSR-7 interface `MessageInterface`. With PSR-7 there is no legal solution to patch this interface to return decoded JSON from `getBody()` method. – Sergey Nevmerzhitsky May 29 '20 at 16:35
  • 4
    A potential solution to the problem @SergeyNevmerzhitsky mentions, could be to turn JsonAwareResponse into a decorator instead, and then implement `__call()` to forward calls to the actual response. Then you can just add in a `getJson()` or whatever you want. – Steen Schütt Jan 04 '22 at 11:22
  • @SergeyNevmerzhitsky Then PSR-7 is a bad standard and should be deprecated and replaced (ths is true for almost all PSR standards, but I digress). To [quote](https://www.destroyallsoftware.com/blog/2018/a-case-study-in-not-being-a-jerk-in-open-source) "There are competent people on standards bodies. But they make mistakes, and the translation of intent to English isn't always perfect. In this case, the standard conflicts with reality, so we ignore the standard. " – chx Jul 18 '23 at 20:35
8

$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 = $response->getBody()->__toString()


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.
simPod
  • 11,498
  • 17
  • 86
  • 139
2

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

You can simply use json_decode

Moh
  • 21
  • 3