226

I'm trying to write a wrapper around an api my company is developing. It's restful, and using Postman I can send a post request to an endpoint like http://subdomain.dev.myapi.com/api/v1/auth/ with a username and password as POST data and I am given back a token. All works as expected. Now, when I try and do the same from PHP I get back a GuzzleHttp\Psr7\Response object, but can't seem to find the token anywhere inside it as I did with the Postman request.

The relevant code looks like:

$client = new Client(['base_uri' => 'http://companysub.dev.myapi.com/']);
$response = $client->post('api/v1/auth/', [
    'form_params' => [
        'username' => $user,
        'password' => $password
    ]
]);

var_dump($response); //or $resonse->getBody(), etc...

The output of the code above looks something like (warning, incoming wall of text):

object(guzzlehttp\psr7\response)#36 (6) {
  ["reasonphrase":"guzzlehttp\psr7\response":private]=>
  string(2) "ok"
  ["statuscode":"guzzlehttp\psr7\response":private]=>
  int(200)
  ["headers":"guzzlehttp\psr7\response":private]=>
  array(9) {
    ["connection"]=>
    array(1) {
      [0]=>
      string(10) "keep-alive"
    }
    ["server"]=>
    array(1) {
      [0]=>
      string(15) "gunicorn/19.3.0"
    }
    ["date"]=>
    array(1) {
      [0]=>
      string(29) "sat, 30 may 2015 17:22:41 gmt"
    }
    ["transfer-encoding"]=>
    array(1) {
      [0]=>
      string(7) "chunked"
    }
    ["content-type"]=>
    array(1) {
      [0]=>
      string(16) "application/json"
    }
    ["allow"]=>
    array(1) {
      [0]=>
      string(13) "post, options"
    }
    ["x-frame-options"]=>
    array(1) {
      [0]=>
      string(10) "sameorigin"
    }
    ["vary"]=>
    array(1) {
      [0]=>
      string(12) "cookie, host"
    }
    ["via"]=>
    array(1) {
      [0]=>
      string(9) "1.1 vegur"
    }
  }
  ["headerlines":"guzzlehttp\psr7\response":private]=>
  array(9) {
    ["connection"]=>
    array(1) {
      [0]=>
      string(10) "keep-alive"
    }
    ["server"]=>
    array(1) {
      [0]=>
      string(15) "gunicorn/19.3.0"
    }
    ["date"]=>
    array(1) {
      [0]=>
      string(29) "sat, 30 may 2015 17:22:41 gmt"
    }
    ["transfer-encoding"]=>
    array(1) {
      [0]=>
      string(7) "chunked"
    }
    ["content-type"]=>
    array(1) {
      [0]=>
      string(16) "application/json"
    }
    ["allow"]=>
    array(1) {
      [0]=>
      string(13) "post, options"
    }
    ["x-frame-options"]=>
    array(1) {
      [0]=>
      string(10) "sameorigin"
    }
    ["vary"]=>
    array(1) {
      [0]=>
      string(12) "cookie, host"
    }
    ["via"]=>
    array(1) {
      [0]=>
      string(9) "1.1 vegur"
    }
  }
  ["protocol":"guzzlehttp\psr7\response":private]=>
  string(3) "1.1"
  ["stream":"guzzlehttp\psr7\response":private]=>
  object(guzzlehttp\psr7\stream)#27 (7) {
    ["stream":"guzzlehttp\psr7\stream":private]=>
    resource(40) of type (stream)
    ["size":"guzzlehttp\psr7\stream":private]=>
    null
    ["seekable":"guzzlehttp\psr7\stream":private]=>
    bool(true)
    ["readable":"guzzlehttp\psr7\stream":private]=>
    bool(true)
    ["writable":"guzzlehttp\psr7\stream":private]=>
    bool(true)
    ["uri":"guzzlehttp\psr7\stream":private]=>
    string(10) "php://temp"
    ["custommetadata":"guzzlehttp\psr7\stream":private]=>
    array(0) {
    }
  }
}

The output from Postman was something like:

{
    "data" : {
        "token" "fasdfasf-asfasdfasdf-sfasfasf"
    }
}

Clearly I'm missing something about working with the response objects in Guzzle. The Guzzle response indicates a 200 status code on the request, so I'm not sure exactly what I need to do to retrieve the returned data.

Federkun
  • 36,084
  • 8
  • 78
  • 90
Greg
  • 6,453
  • 9
  • 45
  • 61

3 Answers3

578

Guzzle implements PSR-7. That means that it will by default store the body of a message in a Stream that uses PHP temp streams. To retrieve all the data, you can use casting operator:

$contents = (string) $response->getBody();

You can also do it with

$contents = $response->getBody()->getContents();

The difference between the two approaches is that getContents returns the remaining contents, so that a second call returns nothing unless you seek the position of the stream with rewind or seek .

$stream = $response->getBody();
$contents = $stream->getContents(); // returns all the contents
$contents = $stream->getContents(); // empty string
$stream->rewind(); // Seek to the beginning
$contents = $stream->getContents(); // returns all the contents

Instead, usings PHP's string casting operations, it will reads all the data from the stream from the beginning until the end is reached.

$contents = (string) $response->getBody(); // returns all the contents
$contents = (string) $response->getBody(); // returns all the contents

Documentation: http://docs.guzzlephp.org/en/latest/psr7.html#responses

Federkun
  • 36,084
  • 8
  • 78
  • 90
  • 6
    The getContents function is only in one small part of the Guzzle 6 documentation (in the streams section), and I missed it. You saved me from a whole lot of searching. – Maximus Sep 04 '15 at 21:55
  • 64
    THANK YOU. It's unbelievable that this is not more clear in the documentation. Even their official documentation (http://docs.guzzlephp.org/en/latest/) makes it seem like calling $res->getBody() returns what you would normally expect. – John Jan 16 '16 at 02:54
  • 26
    They should really put something like a note or notice in the official documents. I wasted two days on this issue. – cwhsu Sep 29 '16 at 02:02
  • +1 [The Guzzle documentation](http://guzzle3.readthedocs.io/http-client/response.html#response-body) falsely states that `"you can pass true to this method [getBody()] to retrieve the body as a string."`. That doesn't seem to work for me using Guzzle 6, but casting to string or using getContents() does work. – Magnus Feb 06 '17 at 04:29
  • @BadCash that's the documentation for guzzle3 – Federkun Feb 06 '17 at 08:56
  • @Federkun I see, thanks! However, that's the documentation that ranks at the top of Google when searching for "guzzle getbody string", so I guess I'm not the only one being led astray by it. Especially since the only thing indicating that it's for a specific older version is a "3" in the URL... – Magnus Feb 06 '17 at 10:04
  • 10
    You can also use json_decode. For example wrap your response in `json_decode($response, true);` this will return an array. – Sygon Feb 06 '18 at 16:40
  • You could also take a look at this example, by using middle-ware: https://stackoverflow.com/questions/30530172/guzzle-6-no-more-json-method-for-responses – Melroy van den Berg Feb 03 '19 at 23:14
  • ```$response->getBody()->getContents();``` returns with a HTTP header in array form. Anyone knows how to fix this? More on this: https://stackoverflow.com/questions/56004396/guzzle-6-result-returns-with-http-headers – Akio May 07 '19 at 06:00
  • 2
    Highly recommend you don't use (string) $response->getBody() as it doesn't actually retrieve all data. It retrieves the length of the default, so it could truncate the actual value. https://github.com/guzzle/guzzle/issues/1610 – Shardj Oct 14 '19 at 15:41
  • @Shardj How can i retrive all content? My content truncating. – Maksim Borodov Mar 03 '21 at 18:08
  • @MaksimBorodov I believe the solution is in the comments of the github issue I linked previously – Shardj Mar 04 '21 at 14:36
  • Finding this answer early in my quest saved me hours of hair pulling. One HUGE gotcha: if you have set up request logging using `Middleware::log()`, and the message formatter uses `{res_body}` anywhere in the format string, then body **WILL** have been read by the time you get the response object, and `$response->getBody()->getContents()` **WILL** return an empty string even though **YOU** haven't read the body! – Szczepan Hołyszewski Apr 22 '22 at 01:06
63

If expecting JSON back, the simplest way to get it:

$data = json_decode($response->getBody()); // returns an object

// OR

$data = json_decode($response->getBody(), true); // returns an array

json_decode() will automatically cast the body to string, so there is no need to call getContents().

Maksim Ivanov
  • 3,991
  • 31
  • 25
  • 2
    This simple answer 'solves' the immediate problem, but it misses the nuance of the accepted answer by @Federkun. The reason this 'just works' most of the time is because the call to `json_decode` **implicitly casts** the response body as a string. Having said that, it is worth understanding the accepted answer, as there can be problems with the string casting approach not always retrieving all data, as highlighted in the comment by @Shardj. – j13k Sep 01 '20 at 06:15
  • 2
    This method works but it will cause errors during static analysis – Вася Аристов Oct 07 '20 at 12:59
13

For get response in JSON format :

1.

$response = (string) $res->getBody();
$response =json_decode($response); // Using this you can access any key like below
$key_value = $response->key_name; //access key  
$response = json_decode($res->getBody(),true);
$key_value =   $response['key_name'];//access key
Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
Bhargav Variya
  • 725
  • 1
  • 10
  • 18