Skip to content

Commit c30387f

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

12 files changed

+472
-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.0 <=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\PromiseInterfacePolyFill<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\PromiseInterfacePolyFill;
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 PromiseInterfacePolyFill($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\PromiseInterfacePolyFill|\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\PromiseInterfacePolyFill|\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\PromiseInterfacePolyFill|\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\PromiseInterfacePolyFill|\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\PromiseInterfacePolyFill|\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\PromiseInterfacePolyFill|\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\PromiseInterfacePolyFill|\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: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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+
* @see PromisorInterface
23+
*
24+
* @internal Used internally for DiscordPHP v10
25+
*
26+
* @since 10.4.0
27+
*/
28+
final class Deferred implements PromisorInterface
29+
{
30+
/**
31+
* The actual Promisor
32+
*/
33+
public ReactDeferred $deferred;
34+
35+
/**
36+
* Determine the installed package is Promise-v3
37+
*/
38+
public static bool $isPromiseV3 = false;
39+
40+
/**
41+
* @var PromiseInterfacePolyFill|PromiseInterfacePolyFill
42+
*/
43+
private $promise;
44+
45+
/**
46+
* @param callable|ReactDeferred $canceller Canceller callback or a Deferred to use
47+
*
48+
* @throws \InvalidArgumentException $canceller is not null or callable or a Deferred
49+
*/
50+
public function __construct($canceller = null)
51+
{
52+
if ($canceller instanceof ReactDeferred) {
53+
$this->deferred = $canceller;
54+
} elseif (null === $canceller || is_callable($canceller)) {
55+
$this->deferred = new ReactDeferred($canceller);
56+
} else {
57+
throw new \InvalidArgumentException('$canceller must be either null or callable or Deferred');
58+
}
59+
}
60+
61+
/**
62+
* @return PromiseInterfacePolyFill|PromiseInterface|\React\Promise\ExtendedPromiseInterface
63+
*/
64+
public function promise()
65+
{
66+
if (!static::$isPromiseV3) {
67+
// Just use the same react/promise v2 promise
68+
return $this->deferred->promise();
69+
}
70+
71+
if (null === $this->promise) {
72+
// Wrap with the polyfill if user installed react/promise v3
73+
$this->promise = new PromiseInterfacePolyFill($this->deferred->promise());
74+
}
75+
76+
return $this->promise;
77+
}
78+
79+
/**
80+
* @see React\Promise\Deferred::resolve()
81+
*
82+
* @return void
83+
*/
84+
public function resolve($value = null)
85+
{
86+
$this->deferred->resolve($value);
87+
}
88+
89+
/**
90+
* @see React\Promise\Deferred::reject()
91+
*
92+
* @param \Throwable $reason required in Promise-v3, will be resolved if not a throwable
93+
*
94+
* @throws \InvalidArgumentException $reason is null & react/promise is v3
95+
*
96+
* @return void
97+
*/
98+
public function reject($reason = null)
99+
{
100+
if (static::$isPromiseV3) {
101+
if (null === $reason) {
102+
$reason = new \InvalidArgumentException('reject($reason) must not be null');
103+
} elseif (!($reason instanceof \Throwable)) {
104+
return $this->deferred->resolve($reason);
105+
}
106+
}
107+
108+
$this->deferred->reject($reason);
109+
}
110+
111+
/**
112+
* Not supported
113+
*
114+
* @deprecated
115+
*/
116+
public function notify($update = null)
117+
{
118+
if (method_exists($this->deferred, 'notify')) {
119+
$this->deferred->notify($update);
120+
return;
121+
}
122+
123+
throw new \BadMethodCallException('notify() is not supported with this polyfill and react/promise v3');
124+
}
125+
126+
/**
127+
* Not supported
128+
*
129+
* @deprecated
130+
*/
131+
public function progress($update = null)
132+
{
133+
if (method_exists($this->deferred, 'progress')) {
134+
$this->deferred->progress($update);
135+
return;
136+
}
137+
138+
throw new \BadMethodCallException('progress() is not supported with this polyfill and react/promise v3');
139+
}
140+
}

0 commit comments

Comments
 (0)