Skip to content

Commit de40371

Browse files
committed
Describe callable arguments for Promise
1 parent bb96a7b commit de40371

File tree

5 files changed

+122
-17
lines changed

5 files changed

+122
-17
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ $promise = new React\Promise\Promise($resolver, $canceller);
318318
```
319319

320320
The promise constructor receives a resolver function and an optional canceller
321-
function which both will be called with 3 arguments:
321+
function which both will be called with two arguments:
322322

323323
* `$resolve($value)` - Primary function that seals the fate of the
324324
returned promise. Accepts either a non-promise value, or another promise.

src/Promise.php

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
*/
1111
final class Promise implements PromiseInterface
1212
{
13-
/** @var ?callable */
13+
/** @var (callable(callable(T):void,callable(\Throwable):void):void)|null */
1414
private $canceller;
1515

1616
/** @var ?PromiseInterface<T> */
1717
private $result;
1818

19-
/** @var callable[] */
19+
/** @var list<callable(PromiseInterface<T>):void> */
2020
private $handlers = [];
2121

2222
/** @var int */
@@ -25,6 +25,10 @@ final class Promise implements PromiseInterface
2525
/** @var bool */
2626
private $cancelled = false;
2727

28+
/**
29+
* @param callable(callable(T):void,callable(\Throwable):void):void $resolver
30+
* @param (callable(callable(T):void,callable(\Throwable):void):void)|null $canceller
31+
*/
2832
public function __construct(callable $resolver, callable $canceller = null)
2933
{
3034
$this->canceller = $canceller;
@@ -57,7 +61,7 @@ public function then(callable $onFulfilled = null, callable $onRejected = null):
5761

5862
return new static(
5963
$this->resolver($onFulfilled, $onRejected),
60-
static function () use (&$parent) {
64+
static function () use (&$parent): void {
6165
assert($parent instanceof self);
6266
--$parent->requiredCancelRequests;
6367

@@ -78,7 +82,7 @@ static function () use (&$parent) {
7882
*/
7983
public function catch(callable $onRejected): PromiseInterface
8084
{
81-
return $this->then(null, static function ($reason) use ($onRejected) {
85+
return $this->then(null, static function (\Throwable $reason) use ($onRejected) {
8286
if (!_checkTypehint($onRejected, $reason)) {
8387
return new RejectedPromise($reason);
8488
}
@@ -92,12 +96,12 @@ public function catch(callable $onRejected): PromiseInterface
9296

9397
public function finally(callable $onFulfilledOrRejected): PromiseInterface
9498
{
95-
return $this->then(static function ($value) use ($onFulfilledOrRejected) {
99+
return $this->then(static function ($value) use ($onFulfilledOrRejected): PromiseInterface {
96100
return resolve($onFulfilledOrRejected())->then(function () use ($value) {
97101
return $value;
98102
});
99-
}, static function ($reason) use ($onFulfilledOrRejected) {
100-
return resolve($onFulfilledOrRejected())->then(function () use ($reason) {
103+
}, static function (\Throwable $reason) use ($onFulfilledOrRejected): PromiseInterface {
104+
return resolve($onFulfilledOrRejected())->then(function () use ($reason): RejectedPromise {
101105
return new RejectedPromise($reason);
102106
});
103107
});
@@ -164,12 +168,12 @@ public function always(callable $onFulfilledOrRejected): PromiseInterface
164168

165169
private function resolver(callable $onFulfilled = null, callable $onRejected = null): callable
166170
{
167-
return function ($resolve, $reject) use ($onFulfilled, $onRejected) {
168-
$this->handlers[] = static function (PromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject) {
171+
return function (callable $resolve, callable $reject) use ($onFulfilled, $onRejected): void {
172+
$this->handlers[] = static function (PromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject): void {
169173
$promise = $promise->then($onFulfilled, $onRejected);
170174

171175
if ($promise instanceof self && $promise->result === null) {
172-
$promise->handlers[] = static function (PromiseInterface $promise) use ($resolve, $reject) {
176+
$promise->handlers[] = static function (PromiseInterface $promise) use ($resolve, $reject): void {
173177
$promise->then($resolve, $reject);
174178
};
175179
} else {
@@ -237,6 +241,9 @@ private function unwrap(PromiseInterface $promise): PromiseInterface
237241
return $promise;
238242
}
239243

244+
/**
245+
* @param callable(callable(mixed):void,callable(\Throwable):void):void $cb
246+
*/
240247
private function call(callable $cb): void
241248
{
242249
// Explicitly overwrite argument with null value. This ensure that this
@@ -274,13 +281,13 @@ private function call(callable $cb): void
274281
$target =& $this;
275282

276283
$callback(
277-
static function ($value) use (&$target) {
284+
static function ($value) use (&$target): void {
278285
if ($target !== null) {
279286
$target->settle(resolve($value));
280287
$target = null;
281288
}
282289
},
283-
static function (\Throwable $reason) use (&$target) {
290+
static function (\Throwable $reason) use (&$target): void {
284291
if ($target !== null) {
285292
$target->reject($reason);
286293
$target = null;

src/functions.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ function resolve($promiseOrValue): PromiseInterface
3535
assert(\is_callable($canceller));
3636
}
3737

38-
return new Promise(function ($resolve, $reject) use ($promiseOrValue): void {
38+
/** @var Promise<T> */
39+
return new Promise(function (callable $resolve, callable $reject) use ($promiseOrValue): void {
3940
$promiseOrValue->then($resolve, $reject);
4041
}, $canceller);
4142
}
@@ -77,7 +78,8 @@ function all(iterable $promisesOrValues): PromiseInterface
7778
{
7879
$cancellationQueue = new Internal\CancellationQueue();
7980

80-
return new Promise(function ($resolve, $reject) use ($promisesOrValues, $cancellationQueue): void {
81+
/** @var Promise<array<T>> */
82+
return new Promise(function (callable $resolve, callable $reject) use ($promisesOrValues, $cancellationQueue): void {
8183
$toResolve = 0;
8284
/** @var bool */
8385
$continue = true;
@@ -129,6 +131,7 @@ function race(iterable $promisesOrValues): PromiseInterface
129131
{
130132
$cancellationQueue = new Internal\CancellationQueue();
131133

134+
/** @var Promise<T> */
132135
return new Promise(function (callable $resolve, callable $reject) use ($promisesOrValues, $cancellationQueue): void {
133136
$continue = true;
134137

@@ -165,7 +168,8 @@ function any(iterable $promisesOrValues): PromiseInterface
165168
{
166169
$cancellationQueue = new Internal\CancellationQueue();
167170

168-
return new Promise(function ($resolve, $reject) use ($promisesOrValues, $cancellationQueue): void {
171+
/** @var Promise<T> */
172+
return new Promise(function (callable $resolve, callable $reject) use ($promisesOrValues, $cancellationQueue): void {
169173
$toReject = 0;
170174
$continue = true;
171175
$reasons = [];

tests/PromiseTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ public function getPromiseTestAdapter(callable $canceller = null): CallbackPromi
1919
{
2020
$resolveCallback = $rejectCallback = null;
2121

22-
$promise = new Promise(function ($resolve, $reject) use (&$resolveCallback, &$rejectCallback) {
22+
$promise = new Promise(function (callable $resolve, callable $reject) use (&$resolveCallback, &$rejectCallback): void {
2323
$resolveCallback = $resolve;
2424
$rejectCallback = $reject;
2525
}, $canceller);
2626

27+
assert(is_callable($resolveCallback));
28+
assert(is_callable($rejectCallback));
29+
2730
return new CallbackPromiseAdapter([
2831
'promise' => function () use ($promise) {
2932
return $promise;

tests/types/promise.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
use React\Promise\Promise;
4+
use function PHPStan\Testing\assertType;
5+
6+
// $promise = new Promise(function (): void { });
7+
// assertType('React\Promise\PromiseInterface<never>', $promise);
8+
9+
// $promise = new Promise(function (callable $resolve): void {
10+
// $resolve(42);
11+
// });
12+
// assertType('React\Promise\PromiseInterface<int>', $promise);
13+
14+
// $promise = new Promise(function (callable $resolve): void {
15+
// $resolve(true);
16+
// $resolve('ignored');
17+
// });
18+
// assertType('React\Promise\PromiseInterface<bool>', $promise);
19+
20+
// $promise = new Promise(function (callable $resolve, callable $reject): void {
21+
// $reject(new \RuntimeException());
22+
// });
23+
// assertType('React\Promise\PromiseInterface<never>', $promise);
24+
25+
// $promise = new Promise(function (): never {
26+
// throw new \RuntimeException();
27+
// });
28+
// assertType('React\Promise\PromiseInterface<never>', $promise);
29+
30+
// invalid number of arguments for $resolver
31+
/** @phpstan-ignore-next-line */
32+
$promise = new Promise(function ($a, $b, $c) { });
33+
assert($promise instanceof Promise);
34+
// assertType('React\Promise\PromiseInterface<never>', $promise);
35+
36+
// invalid types for arguments of $resolver
37+
/** @phpstan-ignore-next-line */
38+
$promise = new Promise(function (int $a, string $b) { });
39+
// assertType('React\Promise\PromiseInterface<never>', $promise);
40+
41+
// invalid number of arguments passed to $resolve
42+
$promise = new Promise(function (callable $resolve) {
43+
/** @phpstan-ignore-next-line */
44+
$resolve();
45+
});
46+
// assertType('React\Promise\PromiseInterface<never>', $promise);
47+
48+
// invalid number of arguments passed to $reject
49+
$promise = new Promise(function (callable $resolve, callable $reject) {
50+
/** @phpstan-ignore-next-line */
51+
$reject();
52+
});
53+
// assertType('React\Promise\PromiseInterface<never>', $promise);
54+
55+
// invalid type passed to $reject
56+
$promise = new Promise(function (callable $resolve, callable $reject) {
57+
/** @phpstan-ignore-next-line */
58+
$reject(2);
59+
});
60+
// assertType('React\Promise\PromiseInterface<never>', $promise);
61+
62+
// invalid number of arguments for $canceller
63+
/** @phpstan-ignore-next-line */
64+
$promise = new Promise(function () { }, function ($a, $b, $c) { });
65+
// assertType('React\Promise\PromiseInterface<never>', $promise);
66+
67+
// invalid types for arguments of $canceller
68+
/** @phpstan-ignore-next-line */
69+
$promise = new Promise(function () { }, function (int $a, string $b) { });
70+
// assertType('React\Promise\PromiseInterface<never>', $promise);
71+
72+
// invalid number of arguments passed to $resolve
73+
$promise = new Promise(function () { }, function (callable $resolve) {
74+
/** @phpstan-ignore-next-line */
75+
$resolve();
76+
});
77+
// assertType('React\Promise\PromiseInterface<never>', $promise);
78+
79+
// invalid number of arguments passed to $reject
80+
$promise = new Promise(function () { }, function (callable $resolve, callable $reject) {
81+
/** @phpstan-ignore-next-line */
82+
$reject();
83+
});
84+
// assertType('React\Promise\PromiseInterface<never>', $promise);
85+
86+
// invalid type passed to $reject
87+
$promise = new Promise(function() { }, function (callable $resolve, callable $reject) {
88+
/** @phpstan-ignore-next-line */
89+
$reject(2);
90+
});
91+
// assertType('React\Promise\PromiseInterface<never>', $promise);

0 commit comments

Comments
 (0)