Skip to content

Commit cf6b150

Browse files
authored
Merge pull request #459 from clue-labs/php8.2
Refactor internal `Transaction` to avoid assigning dynamic properties (PHP 8.2+ compatibility)
2 parents 55ec42a + 8b2c5c8 commit cf6b150

File tree

4 files changed

+59
-42
lines changed

4 files changed

+59
-42
lines changed

src/Io/ClientRequestState.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace React\Http\Io;
4+
5+
/** @internal */
6+
class ClientRequestState
7+
{
8+
/** @var int */
9+
public $numRequests = 0;
10+
11+
/** @var ?\React\Promise\PromiseInterface */
12+
public $pending = null;
13+
14+
/** @var ?\React\EventLoop\TimerInterface */
15+
public $timeout = null;
16+
}

src/Io/Transaction.php

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -67,32 +67,31 @@ public function withOptions(array $options)
6767

6868
public function send(RequestInterface $request)
6969
{
70-
$deferred = new Deferred(function () use (&$deferred) {
71-
if (isset($deferred->pending)) {
72-
$deferred->pending->cancel();
73-
unset($deferred->pending);
70+
$state = new ClientRequestState();
71+
$deferred = new Deferred(function () use ($state) {
72+
if ($state->pending !== null) {
73+
$state->pending->cancel();
74+
$state->pending = null;
7475
}
7576
});
7677

77-
$deferred->numRequests = 0;
78-
7978
// use timeout from options or default to PHP's default_socket_timeout (60)
8079
$timeout = (float)($this->timeout !== null ? $this->timeout : ini_get("default_socket_timeout"));
8180

8281
$loop = $this->loop;
83-
$this->next($request, $deferred)->then(
84-
function (ResponseInterface $response) use ($deferred, $loop, &$timeout) {
85-
if (isset($deferred->timeout)) {
86-
$loop->cancelTimer($deferred->timeout);
87-
unset($deferred->timeout);
82+
$this->next($request, $deferred, $state)->then(
83+
function (ResponseInterface $response) use ($state, $deferred, $loop, &$timeout) {
84+
if ($state->timeout !== null) {
85+
$loop->cancelTimer($state->timeout);
86+
$state->timeout = null;
8887
}
8988
$timeout = -1;
9089
$deferred->resolve($response);
9190
},
92-
function ($e) use ($deferred, $loop, &$timeout) {
93-
if (isset($deferred->timeout)) {
94-
$loop->cancelTimer($deferred->timeout);
95-
unset($deferred->timeout);
91+
function ($e) use ($state, $deferred, $loop, &$timeout) {
92+
if ($state->timeout !== null) {
93+
$loop->cancelTimer($state->timeout);
94+
$state->timeout = null;
9695
}
9796
$timeout = -1;
9897
$deferred->reject($e);
@@ -106,67 +105,65 @@ function ($e) use ($deferred, $loop, &$timeout) {
106105
$body = $request->getBody();
107106
if ($body instanceof ReadableStreamInterface && $body->isReadable()) {
108107
$that = $this;
109-
$body->on('close', function () use ($that, $deferred, &$timeout) {
108+
$body->on('close', function () use ($that, $deferred, $state, &$timeout) {
110109
if ($timeout >= 0) {
111-
$that->applyTimeout($deferred, $timeout);
110+
$that->applyTimeout($deferred, $state, $timeout);
112111
}
113112
});
114113
} else {
115-
$this->applyTimeout($deferred, $timeout);
114+
$this->applyTimeout($deferred, $state, $timeout);
116115
}
117116

118117
return $deferred->promise();
119118
}
120119

121120
/**
122121
* @internal
123-
* @param Deferred $deferred
124-
* @param number $timeout
122+
* @param number $timeout
125123
* @return void
126124
*/
127-
public function applyTimeout(Deferred $deferred, $timeout)
125+
public function applyTimeout(Deferred $deferred, ClientRequestState $state, $timeout)
128126
{
129-
$deferred->timeout = $this->loop->addTimer($timeout, function () use ($timeout, $deferred) {
127+
$state->timeout = $this->loop->addTimer($timeout, function () use ($timeout, $deferred, $state) {
130128
$deferred->reject(new \RuntimeException(
131129
'Request timed out after ' . $timeout . ' seconds'
132130
));
133-
if (isset($deferred->pending)) {
134-
$deferred->pending->cancel();
135-
unset($deferred->pending);
131+
if ($state->pending !== null) {
132+
$state->pending->cancel();
133+
$state->pending = null;
136134
}
137135
});
138136
}
139137

140-
private function next(RequestInterface $request, Deferred $deferred)
138+
private function next(RequestInterface $request, Deferred $deferred, ClientRequestState $state)
141139
{
142140
$this->progress('request', array($request));
143141

144142
$that = $this;
145-
++$deferred->numRequests;
143+
++$state->numRequests;
146144

147145
$promise = $this->sender->send($request);
148146

149147
if (!$this->streaming) {
150-
$promise = $promise->then(function ($response) use ($deferred, $that) {
151-
return $that->bufferResponse($response, $deferred);
148+
$promise = $promise->then(function ($response) use ($deferred, $state, $that) {
149+
return $that->bufferResponse($response, $deferred, $state);
152150
});
153151
}
154152

155-
$deferred->pending = $promise;
153+
$state->pending = $promise;
156154

157155
return $promise->then(
158-
function (ResponseInterface $response) use ($request, $that, $deferred) {
159-
return $that->onResponse($response, $request, $deferred);
156+
function (ResponseInterface $response) use ($request, $that, $deferred, $state) {
157+
return $that->onResponse($response, $request, $deferred, $state);
160158
}
161159
);
162160
}
163161

164162
/**
165163
* @internal
166-
* @param ResponseInterface $response
167164
* @return PromiseInterface Promise<ResponseInterface, Exception>
168165
*/
169-
public function bufferResponse(ResponseInterface $response, $deferred)
166+
public function bufferResponse(ResponseInterface $response, Deferred $deferred, ClientRequestState $state)
170167
{
171168
$stream = $response->getBody();
172169

@@ -205,26 +202,24 @@ function ($e) use ($stream, $maximumSize) {
205202
}
206203
);
207204

208-
$deferred->pending = $promise;
205+
$state->pending = $promise;
209206

210207
return $promise;
211208
}
212209

213210
/**
214211
* @internal
215-
* @param ResponseInterface $response
216-
* @param RequestInterface $request
217212
* @throws ResponseException
218213
* @return ResponseInterface|PromiseInterface
219214
*/
220-
public function onResponse(ResponseInterface $response, RequestInterface $request, $deferred)
215+
public function onResponse(ResponseInterface $response, RequestInterface $request, Deferred $deferred, ClientRequestState $state)
221216
{
222217
$this->progress('response', array($response, $request));
223218

224219
// follow 3xx (Redirection) response status codes if Location header is present and not explicitly disabled
225220
// @link https://tools.ietf.org/html/rfc7231#section-6.4
226221
if ($this->followRedirects && ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400) && $response->hasHeader('Location')) {
227-
return $this->onResponseRedirect($response, $request, $deferred);
222+
return $this->onResponseRedirect($response, $request, $deferred, $state);
228223
}
229224

230225
// only status codes 200-399 are considered to be valid, reject otherwise
@@ -242,19 +237,19 @@ public function onResponse(ResponseInterface $response, RequestInterface $reques
242237
* @return PromiseInterface
243238
* @throws \RuntimeException
244239
*/
245-
private function onResponseRedirect(ResponseInterface $response, RequestInterface $request, Deferred $deferred)
240+
private function onResponseRedirect(ResponseInterface $response, RequestInterface $request, Deferred $deferred, ClientRequestState $state)
246241
{
247242
// resolve location relative to last request URI
248243
$location = Uri::resolve($request->getUri(), $response->getHeaderLine('Location'));
249244

250245
$request = $this->makeRedirectRequest($request, $location);
251246
$this->progress('redirect', array($request));
252247

253-
if ($deferred->numRequests >= $this->maxRedirects) {
248+
if ($state->numRequests >= $this->maxRedirects) {
254249
throw new \RuntimeException('Maximum number of redirects (' . $this->maxRedirects . ') exceeded');
255250
}
256251

257-
return $this->next($request, $deferred);
252+
return $this->next($request, $deferred, $state);
258253
}
259254

260255
/**

tests/HttpServerTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ final class HttpServerTest extends TestCase
1717
private $connection;
1818
private $socket;
1919

20+
/** @var ?int */
21+
private $called = null;
22+
2023
/**
2124
* @before
2225
*/

tests/Io/StreamingServerTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class StreamingServerTest extends TestCase
1717
private $connection;
1818
private $socket;
1919

20+
/** @var ?int */
21+
private $called = null;
22+
2023
/**
2124
* @before
2225
*/

0 commit comments

Comments
 (0)