Skip to content

Commit 605b7c2

Browse files
authored
Merge pull request #84 from php-http/detach
Detach stream before serializing PSR-7 response
2 parents c0c96de + 23b3c85 commit 605b7c2

File tree

4 files changed

+44
-6
lines changed

4 files changed

+44
-6
lines changed

.github/workflows/tests.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ jobs:
7474

7575
- name: Install dependencies
7676
run: |
77-
composer require "friends-of-phpspec/phpspec-code-coverage:^4.3.2" --no-interaction --no-update
77+
composer require "friends-of-phpspec/phpspec-code-coverage" --no-interaction --no-update
7878
composer update --prefer-dist --no-interaction --no-progress
7979
8080
- name: Execute tests

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Change Log
22

3+
## 1.8.0 - 2023-04-28
4+
5+
- Avoid PHP warning about serializing resources when serializing the response by detaching the stream.
6+
37
## 1.7.6 - 2023-04-28
48

59
- Test with PHP 8.1 and 8.2

spec/CachePluginSpec.php

+31
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace spec\Http\Client\Common\Plugin;
44

55
use Http\Client\Common\Plugin\Cache\Generator\SimpleGenerator;
6+
use PhpSpec\Wrapper\Collaborator;
67
use Prophecy\Argument;
78
use Http\Message\StreamFactory;
89
use Http\Promise\FulfilledPromise;
@@ -18,8 +19,14 @@
1819

1920
class CachePluginSpec extends ObjectBehavior
2021
{
22+
/**
23+
* @var StreamFactory&Collaborator
24+
*/
25+
private $streamFactory;
26+
2127
function let(CacheItemPoolInterface $pool, StreamFactory $streamFactory)
2228
{
29+
$this->streamFactory = $streamFactory;
2330
$this->beConstructedWith($pool, $streamFactory, [
2431
'default_ttl' => 60,
2532
'cache_lifetime' => 1000
@@ -42,6 +49,7 @@ function it_caches_responses(CacheItemPoolInterface $pool, CacheItemInterface $i
4249
$stream->__toString()->willReturn($httpBody);
4350
$stream->isSeekable()->willReturn(true);
4451
$stream->rewind()->shouldBeCalled();
52+
$stream->detach()->shouldBeCalled();
4553

4654
$request->getMethod()->willReturn('GET');
4755
$request->getUri()->willReturn($uri);
@@ -53,6 +61,9 @@ function it_caches_responses(CacheItemPoolInterface $pool, CacheItemInterface $i
5361
$response->getHeader('Cache-Control')->willReturn([])->shouldBeCalled();
5462
$response->getHeader('Expires')->willReturn([])->shouldBeCalled();
5563
$response->getHeader('ETag')->willReturn([])->shouldBeCalled();
64+
$response->withBody($stream)->shouldBeCalled()->willReturn($response);
65+
66+
$this->streamFactory->createStream($httpBody)->shouldBeCalled()->willReturn($stream);
5667

5768
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
5869
$item->isHit()->willReturn(false);
@@ -128,6 +139,7 @@ function it_stores_post_requests_when_allowed(
128139
$stream->__toString()->willReturn($httpBody);
129140
$stream->isSeekable()->willReturn(true);
130141
$stream->rewind()->shouldBeCalled();
142+
$stream->detach()->shouldBeCalled();
131143

132144
$request->getMethod()->willReturn('POST');
133145
$request->getUri()->willReturn($uri);
@@ -139,6 +151,9 @@ function it_stores_post_requests_when_allowed(
139151
$response->getHeader('Cache-Control')->willReturn([])->shouldBeCalled();
140152
$response->getHeader('Expires')->willReturn([])->shouldBeCalled();
141153
$response->getHeader('ETag')->willReturn([])->shouldBeCalled();
154+
$response->withBody($stream)->shouldBeCalled()->willReturn($response);
155+
156+
$this->streamFactory->createStream($httpBody)->shouldBeCalled()->willReturn($stream);
142157

143158
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
144159
$item->isHit()->willReturn(false);
@@ -186,6 +201,7 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI
186201
$stream->__toString()->willReturn($httpBody);
187202
$stream->isSeekable()->willReturn(true);
188203
$stream->rewind()->shouldBeCalled();
204+
$stream->detach()->shouldBeCalled();
189205

190206
$request->getMethod()->willReturn('GET');
191207
$request->getUri()->willReturn($uri);
@@ -198,6 +214,9 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI
198214
$response->getHeader('Age')->willReturn(['15']);
199215
$response->getHeader('Expires')->willReturn([]);
200216
$response->getHeader('ETag')->willReturn([]);
217+
$response->withBody($stream)->shouldBeCalled()->willReturn($response);
218+
219+
$this->streamFactory->createStream($httpBody)->shouldBeCalled()->willReturn($stream);
201220

202221
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
203222
$item->isHit()->willReturn(false);
@@ -226,6 +245,7 @@ function it_saves_etag(CacheItemPoolInterface $pool, CacheItemInterface $item, R
226245
$stream->__toString()->willReturn($httpBody);
227246
$stream->isSeekable()->willReturn(true);
228247
$stream->rewind()->shouldBeCalled();
248+
$stream->detach()->shouldBeCalled();
229249
$request->getBody()->shouldBeCalled()->willReturn($stream);
230250

231251
$request->getMethod()->willReturn('GET');
@@ -236,6 +256,9 @@ function it_saves_etag(CacheItemPoolInterface $pool, CacheItemInterface $item, R
236256
$response->getHeader('Cache-Control')->willReturn([]);
237257
$response->getHeader('Expires')->willReturn([]);
238258
$response->getHeader('ETag')->willReturn(['foo_etag']);
259+
$response->withBody($stream)->shouldBeCalled()->willReturn($response);
260+
261+
$this->streamFactory->createStream($httpBody)->shouldBeCalled()->willReturn($stream);
239262

240263
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
241264
$item->isHit()->willReturn(false);
@@ -387,6 +410,7 @@ function it_caches_private_responses_when_allowed(
387410
$stream->__toString()->willReturn($httpBody);
388411
$stream->isSeekable()->willReturn(true);
389412
$stream->rewind()->shouldBeCalled();
413+
$stream->detach()->shouldBeCalled();
390414

391415
$request->getMethod()->willReturn('GET');
392416
$request->getUri()->willReturn($uri);
@@ -398,6 +422,9 @@ function it_caches_private_responses_when_allowed(
398422
$response->getHeader('Cache-Control')->willReturn(['private'])->shouldBeCalled();
399423
$response->getHeader('Expires')->willReturn([])->shouldBeCalled();
400424
$response->getHeader('ETag')->willReturn([])->shouldBeCalled();
425+
$response->withBody($stream)->shouldBeCalled()->willReturn($response);
426+
427+
$this->streamFactory->createStream($httpBody)->shouldBeCalled()->willReturn($stream);
401428

402429
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
403430
$item->isHit()->willReturn(false);
@@ -484,6 +511,7 @@ function it_stores_responses_of_requests_not_in_blacklisted_paths(
484511
$stream->__toString()->willReturn($httpBody);
485512
$stream->isSeekable()->willReturn(true);
486513
$stream->rewind()->shouldBeCalled();
514+
$stream->detach()->shouldBeCalled();
487515

488516
$request->getMethod()->willReturn('GET');
489517
$request->getUri()->willReturn($uri);
@@ -495,6 +523,9 @@ function it_stores_responses_of_requests_not_in_blacklisted_paths(
495523
$response->getHeader('Cache-Control')->willReturn([])->shouldBeCalled();
496524
$response->getHeader('Expires')->willReturn([])->shouldBeCalled();
497525
$response->getHeader('ETag')->willReturn([])->shouldBeCalled();
526+
$response->withBody($stream)->shouldBeCalled()->willReturn($response);
527+
528+
$this->streamFactory->createStream($httpBody)->shouldBeCalled()->willReturn($stream);
498529

499530
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
500531
$item->isHit()->willReturn(false);

src/CachePlugin.php

+8-5
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,7 @@ protected function doHandleRequest(RequestInterface $request, callable $next, ca
195195
if ($this->isCacheable($response) && $this->isCacheableRequest($request)) {
196196
$bodyStream = $response->getBody();
197197
$body = $bodyStream->__toString();
198-
if ($bodyStream->isSeekable()) {
199-
$bodyStream->rewind();
200-
} else {
201-
$response = $response->withBody($this->streamFactory->createStream($body));
202-
}
198+
$bodyStream->detach();
203199

204200
$maxAge = $this->getMaxAge($response);
205201
$cacheItem
@@ -212,6 +208,13 @@ protected function doHandleRequest(RequestInterface $request, callable $next, ca
212208
'etag' => $response->getHeader('ETag'),
213209
]);
214210
$this->pool->save($cacheItem);
211+
212+
$bodyStream = $this->streamFactory->createStream($body);
213+
if ($bodyStream->isSeekable()) {
214+
$bodyStream->rewind();
215+
}
216+
217+
$response = $response->withBody($bodyStream);
215218
}
216219

217220
return $this->handleCacheListeners($request, $response, false, $cacheItem);

0 commit comments

Comments
 (0)