Skip to content

Commit ac1c925

Browse files
committed
Add react/promise v2 to v3 transition helper
1 parent c3327ad commit ac1c925

12 files changed

+463
-32
lines changed

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
}
1111
],
1212
"autoload": {
13+
"files": [
14+
"src/Discord/PromiseHelpers/bootstrap.php"
15+
],
1316
"psr-4": {
1417
"Discord\\Http\\": "src/Discord",
1518
"Tests\\Discord\\Http\\": "tests/Discord"
@@ -19,7 +22,7 @@
1922
"php": "^7.4|^8.0",
2023
"react/http": "^1.2",
2124
"psr/log": "^1.1 || ^2.0 || ^3.0",
22-
"react/promise": "^2.2"
25+
"react/promise": "^2.8 || ^3.1"
2326
},
2427
"suggest": {
2528
"guzzlehttp/guzzle": "For alternative to ReactPHP/Http Browser"

src/Discord/DriverInterface.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
namespace Discord\Http;
1313

1414
use Psr\Http\Message\ResponseInterface;
15-
use React\Promise\ExtendedPromiseInterface;
15+
use React\Promise\PromiseInterface;
1616

1717
/**
1818
* Interface for an HTTP driver.
@@ -28,7 +28,7 @@ interface DriverInterface
2828
*
2929
* @param Request $request
3030
*
31-
* @return ExtendedPromiseInterface<ResponseInterface>
31+
* @return \Discord\Http\PromiseHelpers\PolyFillPromiseInterface<ResponseInterface>|\React\Promise\ExtendedPromiseInterface<ResponseInterface>
3232
*/
33-
public function runRequest(Request $request): ExtendedPromiseInterface;
33+
public function runRequest(Request $request): PromiseInterface;
3434
}

src/Discord/Drivers/Guzzle.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
namespace Discord\Http\Drivers;
1313

1414
use Discord\Http\DriverInterface;
15+
use Discord\Http\PromiseHelpers\Deferred;
1516
use Discord\Http\Request;
1617
use GuzzleHttp\Client;
1718
use GuzzleHttp\RequestOptions;
1819
use React\EventLoop\LoopInterface;
19-
use React\Promise\Deferred;
20-
use React\Promise\ExtendedPromiseInterface;
20+
use React\Promise\PromiseInterface;
2121

2222
/**
2323
* guzzlehttp/guzzle driver for Discord HTTP client. (still with React Promise).
@@ -55,7 +55,7 @@ public function __construct(?LoopInterface $loop = null, array $options = [])
5555
$this->client = new Client($options);
5656
}
5757

58-
public function runRequest(Request $request): ExtendedPromiseInterface
58+
public function runRequest(Request $request): PromiseInterface
5959
{
6060
// Create a React promise
6161
$deferred = new Deferred();

src/Discord/Drivers/React.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
namespace Discord\Http\Drivers;
1313

1414
use Discord\Http\DriverInterface;
15+
use Discord\Http\PromiseHelpers\PolyFillPromiseInterface;
1516
use Discord\Http\Request;
1617
use React\EventLoop\LoopInterface;
1718
use React\Http\Browser;
18-
use React\Promise\ExtendedPromiseInterface;
19+
use React\Promise\PromiseInterface;
1920
use React\Socket\Connector;
2021

2122
/**
@@ -54,12 +55,12 @@ public function __construct(LoopInterface $loop, array $options = [])
5455
$this->browser = $browser->withRejectErrorResponse(false);
5556
}
5657

57-
public function runRequest(Request $request): ExtendedPromiseInterface
58+
public function runRequest(Request $request): PromiseInterface
5859
{
59-
return $this->browser->{$request->getMethod()}(
60+
return new PolyFillPromiseInterface($this->browser->{$request->getMethod()}(
6061
$request->getUrl(),
6162
$request->getHeaders(),
6263
$request->getContent()
63-
);
64+
));
6465
}
6566
}

src/Discord/Http.php

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
use Discord\Http\Exceptions\NotFoundException;
2020
use Discord\Http\Exceptions\RateLimitException;
2121
use Discord\Http\Exceptions\RequestFailedException;
22+
use Discord\Http\PromiseHelpers\Deferred;
2223
use Discord\Http\Multipart\MultipartBody;
2324
use Psr\Http\Message\ResponseInterface;
2425
use Psr\Log\LoggerInterface;
2526
use React\EventLoop\LoopInterface;
26-
use React\Promise\Deferred;
27-
use React\Promise\ExtendedPromiseInterface;
27+
use React\Promise\PromiseInterface;
2828
use SplQueue;
2929

3030
/**
@@ -39,7 +39,7 @@ class Http
3939
*
4040
* @var string
4141
*/
42-
public const VERSION = 'v10.3.0';
42+
public const VERSION = 'v10.4.0';
4343

4444
/**
4545
* Current Discord HTTP API version.
@@ -160,9 +160,9 @@ public function setDriver(DriverInterface $driver): void
160160
* @param mixed $content
161161
* @param array $headers
162162
*
163-
* @return ExtendedPromiseInterface
163+
* @return \Discord\Http\PromiseHelpers\PolyFillPromiseInterface|\React\Promise\ExtendedPromiseInterface
164164
*/
165-
public function get($url, $content = null, array $headers = []): ExtendedPromiseInterface
165+
public function get($url, $content = null, array $headers = []): PromiseInterface
166166
{
167167
if (! ($url instanceof Endpoint)) {
168168
$url = Endpoint::bind($url);
@@ -178,9 +178,9 @@ public function get($url, $content = null, array $headers = []): ExtendedPromise
178178
* @param mixed $content
179179
* @param array $headers
180180
*
181-
* @return ExtendedPromiseInterface
181+
* @return \Discord\Http\PromiseHelpers\PolyFillPromiseInterface|\React\Promise\ExtendedPromiseInterface
182182
*/
183-
public function post($url, $content = null, array $headers = []): ExtendedPromiseInterface
183+
public function post($url, $content = null, array $headers = []): PromiseInterface
184184
{
185185
if (! ($url instanceof Endpoint)) {
186186
$url = Endpoint::bind($url);
@@ -196,9 +196,9 @@ public function post($url, $content = null, array $headers = []): ExtendedPromis
196196
* @param mixed $content
197197
* @param array $headers
198198
*
199-
* @return ExtendedPromiseInterface
199+
* @return \Discord\Http\PromiseHelpers\PolyFillPromiseInterface|\React\Promise\ExtendedPromiseInterface
200200
*/
201-
public function put($url, $content = null, array $headers = []): ExtendedPromiseInterface
201+
public function put($url, $content = null, array $headers = []): PromiseInterface
202202
{
203203
if (! ($url instanceof Endpoint)) {
204204
$url = Endpoint::bind($url);
@@ -214,9 +214,9 @@ public function put($url, $content = null, array $headers = []): ExtendedPromise
214214
* @param mixed $content
215215
* @param array $headers
216216
*
217-
* @return ExtendedPromiseInterface
217+
* @return \Discord\Http\PromiseHelpers\PolyFillPromiseInterface|\React\Promise\ExtendedPromiseInterface
218218
*/
219-
public function patch($url, $content = null, array $headers = []): ExtendedPromiseInterface
219+
public function patch($url, $content = null, array $headers = []): PromiseInterface
220220
{
221221
if (! ($url instanceof Endpoint)) {
222222
$url = Endpoint::bind($url);
@@ -232,9 +232,9 @@ public function patch($url, $content = null, array $headers = []): ExtendedPromi
232232
* @param mixed $content
233233
* @param array $headers
234234
*
235-
* @return ExtendedPromiseInterface
235+
* @return \Discord\Http\PromiseHelpers\PolyFillPromiseInterface|\React\Promise\ExtendedPromiseInterface
236236
*/
237-
public function delete($url, $content = null, array $headers = []): ExtendedPromiseInterface
237+
public function delete($url, $content = null, array $headers = []): PromiseInterface
238238
{
239239
if (! ($url instanceof Endpoint)) {
240240
$url = Endpoint::bind($url);
@@ -251,9 +251,9 @@ public function delete($url, $content = null, array $headers = []): ExtendedProm
251251
* @param mixed $content
252252
* @param array $headers
253253
*
254-
* @return ExtendedPromiseInterface
254+
* @return \Discord\Http\PromiseHelpers\PolyFillPromiseInterface|\React\Promise\ExtendedPromiseInterface
255255
*/
256-
public function queueRequest(string $method, Endpoint $url, $content, array $headers = []): ExtendedPromiseInterface
256+
public function queueRequest(string $method, Endpoint $url, $content, array $headers = []): PromiseInterface
257257
{
258258
$deferred = new Deferred();
259259

@@ -318,16 +318,16 @@ protected function guessContent(&$content)
318318
* @param Request $request
319319
* @param Deferred $deferred
320320
*
321-
* @return ExtendedPromiseInterface
321+
* @return \Discord\Http\PromiseHelpers\PolyFillPromiseInterface|\React\Promise\ExtendedPromiseInterface
322322
*/
323-
protected function executeRequest(Request $request, Deferred $deferred = null): ExtendedPromiseInterface
323+
protected function executeRequest(Request $request, Deferred $deferred = null): PromiseInterface
324324
{
325325
if ($deferred === null) {
326326
$deferred = new Deferred();
327327
}
328328

329329
if ($this->rateLimit) {
330-
$deferred->reject($this->rateLimit);
330+
$deferred->reject($this->rateLimit); // TODO handle resolve ratelimit
331331

332332
return $deferred->promise();
333333
}
@@ -383,7 +383,7 @@ protected function executeRequest(Request $request, Deferred $deferred = null):
383383
});
384384
}
385385

386-
$deferred->reject($rateLimit->isGlobal() ? $this->rateLimit : $rateLimit);
386+
$deferred->reject($rateLimit->isGlobal() ? $this->rateLimit : $rateLimit); // TODO handle resolve ratelimit
387387
}
388388
// Bad Gateway
389389
// Cloudflare SSL Handshake error
@@ -476,7 +476,7 @@ protected function checkQueue(): void
476476
--$this->waiting;
477477
$this->checkQueue();
478478
$deferred->resolve($result);
479-
}, function ($e) use ($deferred) {
479+
}, function ($e) use ($deferred) { // TODO handle resolve reject
480480
--$this->waiting;
481481
$this->checkQueue();
482482
$deferred->reject($e);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
/*
3+
* This file is a part of the DiscordPHP-Http project.
4+
*
5+
* Copyright (c) 2021-present David Cole <[email protected]>
6+
*
7+
* This file is subject to the MIT license that is bundled
8+
* with this source code in the LICENSE file.
9+
*/
10+
11+
namespace Discord\Http\PromiseHelpers;
12+
13+
/**
14+
* A transition helper from react/promise v2 to react/promise v3.
15+
* Please do not use this polyfill class in place of real CancellablePromiseInterface.
16+
*
17+
* @see \React\Promise\CancellablePromiseInterface
18+
*
19+
* @internal Used internally for DiscordPHP v10
20+
*
21+
* @since 10.4.0
22+
*/
23+
interface CancellablePromiseInterface
24+
{
25+
public function cancel();
26+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<?php
2+
3+
/*
4+
* This file is a part of the DiscordPHP-Http project.
5+
*
6+
* Copyright (c) 2021-present David Cole <[email protected]>
7+
*
8+
* This file is subject to the MIT license that is bundled
9+
* with this source code in the LICENSE file.
10+
*/
11+
12+
namespace Discord\Http\PromiseHelpers;
13+
14+
use React\Promise\Deferred as ReactDeferred;
15+
use React\Promise\PromiseInterface;
16+
17+
/**
18+
* A transition helper from react/promise v2 to react/promise v3.
19+
* Please do not use this polyfill class in place of real Deferred.
20+
*
21+
* @see \React\Promise\Deferred
22+
*
23+
* @internal Used internally for DiscordPHP v10
24+
*
25+
* @since 10.4.0
26+
*/
27+
final class Deferred implements PromisorInterface
28+
{
29+
/**
30+
* The actual Promisor
31+
*/
32+
public ReactDeferred $deferred;
33+
34+
/**
35+
* Determine the installed package is Promise-v3
36+
*/
37+
public static bool $isPromiseV3 = false;
38+
39+
/**
40+
* @var PolyFillPromiseInterface|PolyFillPromiseInterface
41+
*/
42+
private $promise;
43+
44+
/**
45+
* @param callable|ReactDeferred $canceller Canceller callback or a Deferred to use
46+
*
47+
* @throws \InvalidArgumentException $canceller is not null or callable or a Deferred
48+
*/
49+
public function __construct($canceller = null)
50+
{
51+
if ($canceller instanceof ReactDeferred) {
52+
$this->deferred = $canceller;
53+
} elseif (null === $canceller || is_callable($canceller)) {
54+
$this->deferred = new ReactDeferred($canceller);
55+
} else {
56+
throw new \InvalidArgumentException('$canceller must be either null or callable or Deferred');
57+
}
58+
}
59+
60+
/**
61+
* @return PolyFillPromiseInterface|PromiseInterface|\React\Promise\ExtendedPromiseInterface
62+
*/
63+
public function promise()
64+
{
65+
if (!static::$isPromiseV3) {
66+
// Just use the same react/promise v2 promise
67+
return $this->deferred->promise();
68+
}
69+
70+
if (null === $this->promise) {
71+
// Wrap with the polyfill if user installed react/promise v3
72+
$this->promise = new PolyFillPromiseInterface($this->deferred->promise());
73+
}
74+
75+
return $this->promise;
76+
}
77+
78+
/**
79+
* @see React\Promise\Deferred::resolve()
80+
*
81+
* @return void
82+
*/
83+
public function resolve($value = null)
84+
{
85+
$this->deferred->resolve($value);
86+
}
87+
88+
/**
89+
* @see React\Promise\Deferred::reject()
90+
*
91+
* @param \Throwable $reason required in Promise-v3, will be resolved if not a throwable
92+
*
93+
* @throws \InvalidArgumentException $reason is null & react/promise is v3
94+
*
95+
* @return void
96+
*/
97+
public function reject($reason = null)
98+
{
99+
if (static::$isPromiseV3) {
100+
if (null === $reason) {
101+
$reason = new \InvalidArgumentException('reject($reason) must not be null');
102+
} elseif (!($reason instanceof \Throwable)) {
103+
return $this->deferred->resolve($reason);
104+
}
105+
}
106+
107+
$this->deferred->reject($reason);
108+
}
109+
110+
/**
111+
* Not supported
112+
*
113+
* @deprecated
114+
*/
115+
public function notify($update = null)
116+
{
117+
if (method_exists($this->deferred, 'notify')) {
118+
$this->deferred->notify($update);
119+
return;
120+
}
121+
122+
throw new \BadMethodCallException('notify() is not supported with this polyfill and react/promise v3');
123+
}
124+
125+
/**
126+
* Not supported
127+
*
128+
* @deprecated
129+
*/
130+
public function progress($update = null)
131+
{
132+
if (method_exists($this->deferred, 'progress')) {
133+
$this->deferred->progress($update);
134+
return;
135+
}
136+
137+
throw new \BadMethodCallException('progress() is not supported with this polyfill and react/promise v3');
138+
}
139+
}

0 commit comments

Comments
 (0)