Featured image of post GuzzleHttp getContents() Returns Empty on the Second Call

GuzzleHttp getContents() Returns Empty on the Second Call

Casting a GuzzleHttp Response to string auto-seeks to 0, but getContents() never resets the pointer, so the second call returns empty. Always rewind manually.

Two Ways to Get the Body

After getting a Response from GuzzleHttp PSR-7, there are two ways to retrieve the content:

1
2
3
4
5
// Method 1: Cast to string
(string) $response->getBody();

// Method 2: getContents()
$response->getBody()->getContents();

On the first call, both return the same result. But calling them twice reveals a difference.

The Difference

1
2
3
4
5
6
7
// Method 1: Both calls return content
(string) $response->getBody(); // has content
(string) $response->getBody(); // has content

// Method 2: Second call returns empty
$response->getBody()->getContents(); // has content
$response->getBody()->getContents(); // empty string

Why

Looking at the source code makes it clear. __toString calls seek(0) to reset the stream pointer, so casting to string always reads the full content:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Stream implements StreamInterface
{
    public function __toString()
    {
        try {
            $this->seek(0); // Reset pointer
            return (string) stream_get_contents($this->stream);
        } catch (\Exception $e) {
            return '';
        }
    }
}

getContents() does not reset the pointer – it reads from the current position forward. After the first read, the pointer is at the end, so the second read returns nothing.

If you need to use getContents() and read multiple times, call $response->getBody()->rewind() first.