Skip to content

Commit 9eccea6

Browse files
committed
Use php://temp stream wrapper for storing responses
1 parent 13a61d0 commit 9eccea6

File tree

4 files changed

+67
-29
lines changed

4 files changed

+67
-29
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
Semantic versioning 2.0 is followed.
44

5+
# 1.1.0
6+
7+
- Use php://temp stream wrapper for storing responses
8+
59
# 1.0.1
610

711
- Streamed response body and headers were not cleared for every request

src/Client.php

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Psr\Http\Message\ResponseInterface;
1313
use Psr\Http\Message\StreamFactoryInterface;
1414
use Psr\Http\Message\UriInterface;
15+
use SharkMachine\Psr18Shark\Exception\ClientException;
1516
use SharkMachine\Psr18Shark\Exception\CurlException;
1617
use SharkMachine\Psr18Shark\Exception\NoResponseException;
1718
use SharkMachine\Psr18Shark\Handler\RequestMutationHandlerCollection;
@@ -28,16 +29,6 @@ class Client implements ClientInterface
2829
*/
2930
private CurlHandle $curl;
3031

31-
/**
32-
* @var string
33-
*/
34-
private string $dataStream = '';
35-
36-
/**
37-
* @var array<string, string[]>
38-
*/
39-
private array $responseHeaders = [];
40-
4132
/**
4233
* @param ResponseFactoryInterface $responseFactory
4334
* @param StreamFactoryInterface $streamFactory
@@ -79,10 +70,10 @@ public function sendRequest(RequestInterface $request): ResponseInterface
7970
}
8071
}
8172

82-
$this->initCurl($request->getUri());
8373
try {
84-
$this->curlRequest($request);
85-
$response = $this->getResponse();
74+
$responseData = $this->initCurl($request->getUri());
75+
$this->curlRequest($request, $responseData);
76+
$response = $this->getResponse($responseData);
8677
} catch (Throwable $ex) {
8778
curl_close($this->curl);
8879
if (null !== $this->transferHandlerCollection) {
@@ -111,11 +102,18 @@ public function sendRequest(RequestInterface $request): ResponseInterface
111102
/**
112103
* @param UriInterface $uri
113104
*
114-
* @return void
105+
* @return ResponseData
106+
*
107+
* @throws ClientException
115108
*/
116-
protected function initCurl(UriInterface $uri): void
109+
protected function initCurl(UriInterface $uri): ResponseData
117110
{
118111
$this->curl = curl_init((string)$uri);
112+
$dataStream = fopen('php://temp', 'wb+');
113+
if (false === $dataStream) {
114+
throw new ClientException('Unable to open handle for response');
115+
}
116+
$responseData = new ResponseData($dataStream);
119117

120118
// Do not follow redirects.
121119
curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, false);
@@ -124,28 +122,31 @@ protected function initCurl(UriInterface $uri): void
124122
curl_setopt(
125123
$this->curl,
126124
CURLOPT_WRITEFUNCTION,
127-
function (CurlHandle $curl, string $data): int {
128-
$this->dataStream .= $data;
129-
return strlen($data);
125+
static function (CurlHandle $curl, string $data) use ($responseData): int {
126+
$bytes = fwrite($responseData->streamHandle, $data);
127+
if (false === $bytes) {
128+
return -1; // This will cause a cURL error
129+
}
130+
return $bytes;
130131
}
131132
);
132133

133134
// Get headers
134135
curl_setopt(
135136
$this->curl,
136137
CURLOPT_HEADERFUNCTION,
137-
function (CurlHandle $curl, string $header): int {
138+
static function (CurlHandle $curl, string $header) use ($responseData): int {
138139
$len = strlen($header);
139140
$headerArray = explode(':', $header, 2);
140141
if (count($headerArray) < 2) {
141142
return $len;
142143
}
143144
$headerName = strtolower(trim($headerArray[0]));
144145
if (!str_contains($headerArray[1], ',')) {
145-
$this->responseHeaders[$headerName] = [trim($headerArray[1])];
146+
$responseData->headers[$headerName] = [trim($headerArray[1])];
146147
return $len;
147148
}
148-
$this->responseHeaders[$headerName] = array_map(
149+
$responseData->headers[$headerName] = array_map(
149150
'trim',
150151
explode(',', $headerArray[1])
151152
);
@@ -175,20 +176,20 @@ function (CurlHandle $curl, string $header): int {
175176
if (!in_array(CURLOPT_USERAGENT, $this->curlOptions, true)) {
176177
curl_setopt($this->curl, CURLOPT_USERAGENT, self::DEFAULT_USER_AGENT);
177178
}
179+
180+
return $responseData;
178181
}
179182

180183
/**
181184
* @param RequestInterface $request
185+
* @param ResponseData $responseData
182186
*
183-
* @return void
187+
* @return ResponseData
184188
*
185189
* @throws ClientExceptionInterface
186190
*/
187-
protected function curlRequest(RequestInterface $request): void
191+
protected function curlRequest(RequestInterface $request, ResponseData $responseData): ResponseData
188192
{
189-
$this->dataStream = '';
190-
$this->responseHeaders = [];
191-
192193
if (count($request->getHeaders()) > 0) {
193194
$headers = [];
194195
foreach ($request->getHeaders() as $headerName => $headerValues) {
@@ -209,19 +210,23 @@ protected function curlRequest(RequestInterface $request): void
209210
if (false === curl_exec($this->curl)) {
210211
throw new CurlException(curl_error($this->curl));
211212
}
213+
return $responseData;
212214
}
213215

214216
/**
217+
* @param ResponseData $responseData
218+
*
215219
* @return ResponseInterface
216220
*/
217-
protected function getResponse(): ResponseInterface
221+
protected function getResponse(ResponseData $responseData): ResponseInterface
218222
{
219223
$statusCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
220224
$response = $this->responseFactory->createResponse($statusCode);
221-
$response = $response->withBody($this->streamFactory->createStream($this->dataStream));
222-
foreach ($this->responseHeaders as $headerName => $headerValue) {
225+
$response = $response->withBody($this->streamFactory->createStreamFromResource($responseData->streamHandle));
226+
foreach ($responseData->headers as $headerName => $headerValue) {
223227
$response = $response->withHeader($headerName, $headerValue);
224228
}
229+
$response->getBody()->rewind();
225230
if (null !== $this->responseMutationHandlerCollection) {
226231
foreach ($this->responseMutationHandlerCollection as $handler) {
227232
$response = $handler->handleResponse($response);

src/Exception/ClientException.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SharkMachine\Psr18Shark\Exception;
6+
7+
use Exception;
8+
use Psr\Http\Client\ClientExceptionInterface;
9+
10+
final class ClientException extends Exception implements ClientExceptionInterface
11+
{
12+
}

src/ResponseData.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace SharkMachine\Psr18Shark;
5+
6+
class ResponseData
7+
{
8+
/**
9+
* @param resource $streamHandle
10+
* @param array<string, string[]> $headers
11+
*/
12+
public function __construct(
13+
public $streamHandle,
14+
public array $headers = []
15+
) {
16+
}
17+
}

0 commit comments

Comments
 (0)