diff --git a/src/CacheEntry.php b/src/CacheEntry.php index 7e3c8bc..ebeeaf3 100644 --- a/src/CacheEntry.php +++ b/src/CacheEntry.php @@ -3,10 +3,11 @@ namespace Kevinrob\GuzzleCache; use GuzzleHttp\Psr7\PumpStream; +use Psr\Http\Message\MessageInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -class CacheEntry +class CacheEntry implements \Serializable { /** * @var RequestInterface @@ -256,47 +257,72 @@ public function getAge() return time() - $this->dateCreated->getTimestamp(); } - public function __sleep() + public function __serialize(): array { - // Stream/Resource can't be serialized... So we copy the content into an implementation of `Psr\Http\Message\StreamInterface` - if ($this->response !== null) { - $responseBody = (string)$this->response->getBody(); - $this->response = $this->response->withBody( - new PumpStream( - new BodyStore($responseBody), - [ - 'size' => mb_strlen($responseBody), - ] - ) - ); - } + return [ + 'request' => self::toSerializeableMessage($this->request), + 'response' => $this->response !== null ? self::toSerializeableMessage($this->response) : null, + 'staleAt' => $this->staleAt, + 'staleIfErrorTo' => $this->staleIfErrorTo, + 'staleWhileRevalidateTo' => $this->staleWhileRevalidateTo, + 'dateCreated' => $this->dateCreated, + 'timestampStale' => $this->timestampStale, + ]; + } + + public function __unserialize(array $data): void + { + $this->request = self::restoreStreamBody($data['request']); + $this->response = $data['response'] !== null ? self::restoreStreamBody($data['response']) : null; + $this->staleAt = $data['staleAt']; + $this->staleIfErrorTo = $data['staleIfErrorTo']; + $this->staleWhileRevalidateTo = $data['staleWhileRevalidateTo']; + $this->dateCreated = $data['dateCreated']; + $this->timestampStale = $data['timestampStale']; + } + + /** + * Stream/Resource can't be serialized... So we copy the content into an implementation of `Psr\Http\Message\StreamInterface` + * + * @template T of MessageInterface + * + * @param T $message + * @return T + */ + private static function toSerializeableMessage(MessageInterface $message): MessageInterface + { + $bodyString = (string)$message->getBody(); - $requestBody = (string)$this->request->getBody(); - $this->request = $this->request->withBody( + return $message->withBody( new PumpStream( - new BodyStore($requestBody), + new BodyStore($bodyString), [ - 'size' => mb_strlen($requestBody) + 'size' => mb_strlen($bodyString), ] ) ); + } - return array_keys(get_object_vars($this)); + /** + * @template T of MessageInterface + * + * @param T $message + * @return T + */ + private static function restoreStreamBody(MessageInterface $message): MessageInterface + { + return $message->withBody( + \GuzzleHttp\Psr7\Utils::streamFor((string) $message->getBody()) + ); } - public function __wakeup() + public function serialize() { - // We re-create the stream of the response - if ($this->response !== null) { - $this->response = $this->response - ->withBody( - \GuzzleHttp\Psr7\Utils::streamFor((string) $this->response->getBody()) - ); - } - $this->request = $this->request - ->withBody( - \GuzzleHttp\Psr7\Utils::streamFor((string) $this->request->getBody()) - ); + return serialize($this->__serialize()); } + public function unserialize($data) + { + $this->__unserialize(unserialize($data)); + } } diff --git a/tests/CacheEntryTest.php b/tests/CacheEntryTest.php index 21ed04f..833ad78 100644 --- a/tests/CacheEntryTest.php +++ b/tests/CacheEntryTest.php @@ -111,6 +111,29 @@ public function testCacheEntryShouldBeSerializableWithIgBinaryWithoutWarning() } } + public function testSerializationShouldNotMutateCacheEntry() + { + $request = new Request( + 'GET', + 'test.local', + [], + 'Sample body' // Always include a body in the request to be sure there is a stream in it + ); + $response = new Response( + 200, [ + 'Cache-Control' => 'max-age=60', + ], + 'Test content' + ); + $cacheEntry = new CacheEntry($request, $response, $this->makeDateTimeOffset(10)); + + $originalCacheEntry = clone $cacheEntry; + + serialize($cacheEntry); + + $this->assertEquals($cacheEntry, $originalCacheEntry); + } + private function setResponseHeader($name, $value) { $this->responseHeaders[$name] = [$value];