Codebase list php-react-promise / 95964c8
Import upstream version 2.9.0+git20221031.1.863c4a6 Debian Janitor 1 year, 6 months ago
61 changed file(s) with 2347 addition(s) and 4364 deletion(s). Raw diff Collapse all Expand all
+0
-46
.github/workflows/ci.yml less more
0 name: CI
1
2 on:
3 push:
4 pull_request:
5
6 jobs:
7 PHPUnit:
8 name: PHPUnit (PHP ${{ matrix.php }})
9 runs-on: ubuntu-20.04
10 strategy:
11 matrix:
12 php:
13 - 8.1
14 - 8.0
15 - 7.4
16 - 7.3
17 - 7.2
18 - 7.1
19 - 7.0
20 - 5.6
21 - 5.5
22 - 5.4
23 steps:
24 - uses: actions/checkout@v2
25 - uses: shivammathur/setup-php@v2
26 with:
27 php-version: ${{ matrix.php }}
28 coverage: xdebug
29 - run: composer install
30 - run: vendor/bin/phpunit --coverage-text
31 if: ${{ matrix.php >= 7.3 }}
32 - run: vendor/bin/phpunit --coverage-text -c phpunit.xml.legacy
33 if: ${{ matrix.php < 7.3 }}
34
35 PHPUnit-hhvm:
36 name: PHPUnit (HHVM)
37 runs-on: ubuntu-18.04
38 continue-on-error: true
39 steps:
40 - uses: actions/checkout@v2
41 - uses: azjezz/setup-hhvm@v1
42 with:
43 version: lts-3.30
44 - run: hhvm $(which composer) install
45 - run: hhvm vendor/bin/phpunit
+0
-2
.gitignore less more
0 /composer.lock
1 /vendor/
0 CHANGELOG for 2.x
0 CHANGELOG for 3.x
11 =================
22
3 * 2.9.0 (2022-02-11)
3 * 3.0.0 (xxxx-xx-xx)
44
5 * Feature: Support union types and address deprecation of `ReflectionType::getClass()` (PHP 8+).
6 (#198 by @cdosoftei and @SimonFrings)
5 New major release.
76
8 ```php
9 $promise->otherwise(function (OverflowException|UnderflowException $e) {
10 echo 'Error: ' . $e->getMessage() . PHP_EOL;
11 });
12 ```
7 * Introduce a global task queue to eliminate recursion and reduce stack size
8 when chaining promises (#28).
9 * BC break: The progression API has been removed (#32).
10 * BC break: The promise-array related functions (`all()`, `race()`, `any()`,
11 `some()`, `map()`, `reduce()`) now require an array of promises or values
12 as input. Before, arrays and promises which resolve to an array were
13 supported, other input types resolved to empty arrays or `null` (#35).
14 * BC break: `race()` now returns a forever pending promise when called with
15 an empty array (#83).
16 The behavior is now also in line with the ES6 specification.
17 * BC break: The interfaces `PromiseInterface`, `ExtendedPromiseInterface`
18 and `CancellablePromiseInterface` have been merged into a single
19 `PromiseInterface` (#75).
1320
14 * Feature: Support intersection types (PHP 8.1+).
15 (#195 by @bzikarsky)
16
17 ```php
18 $promise->otherwise(function (OverflowException&CacheException $e) {
19 echo 'Error: ' . $e->getMessage() . PHP_EOL;
20 });
21 ```
22
23 * Improve test suite, use GitHub actions for continuous integration (CI),
24 update to PHPUnit 9, and add full core team to the license.
25 (#174, #183, #186, and #201 by @SimonFrings and #211 by @clue)
26
27 * 2.8.0 (2020-05-12)
28
29 * Mark `FulfilledPromise`, `RejectedPromise` and `LazyPromise` as deprecated for Promise v2 (and remove for Promise v3).
30 (#143 and #165 by @clue)
31
32 ```php
33 // deprecated
34 $fulfilled = new React\Promise\FulfilledPromise($value);
35 $rejected = new React\Promise\RejectedPromise($reason);
36
37 // recommended alternatives
38 $fulfilled = React\Promise\resolve($value);
39 $rejected = React\Promise\reject($reason);
40 ```
41
42 * Fix: Fix checking whether cancellable promise is an object and avoid possible warning.
43 (#168 by @smscr and @jsor)
44
45 * Improve documentation and add docblocks to functions and interfaces.
46 (#135 by @CharlotteDunois)
47
48 * Add `.gitattributes` to exclude dev files from exports.
49 (#154 by @reedy)
50
51 * Improve test suite, run tests on PHP 7.4 and update PHPUnit test setup.
52 (#163 by @clue)
53
54 * 2.7.1 (2018-01-07)
55
56 * Fix: file_exists warning when resolving with long strings.
57 (#130 by @sbesselsen)
58 * Improve performance by prefixing all global functions calls with \ to skip the look up and resolve process and go straight to the global function.
59 (#133 by @WyriHaximus)
60
61 * 2.7.0 (2018-06-13)
62
63 * Feature: Improve memory consumption for pending promises by using static internal callbacks without binding to self.
64 (#124 by @clue)
65
66 * 2.6.0 (2018-06-11)
67
68 * Feature: Significantly improve memory consumption and performance by only passing resolver args
69 to resolver and canceller if callback requires them. Also use static callbacks without
70 binding to promise, clean up canceller function reference when they are no longer
71 needed and hide resolver and canceller references from call stack on PHP 7+.
72 (#113, #115, #116, #117, #118, #119 and #123 by @clue)
73
74 These changes combined mean that rejecting promises with an `Exception` should
75 no longer cause any internal circular references which could cause some unexpected
76 memory growth in previous versions. By explicitly avoiding and explicitly
77 cleaning up said references, we can avoid relying on PHP's circular garbage collector
78 to kick in which significantly improves performance when rejecting many promises.
79
80 * Mark legacy progress support / notification API as deprecated
81 (#112 by @clue)
82
83 * Recommend rejecting promises by throwing an exception
84 (#114 by @jsor)
85
86 * Improve documentation to properly instantiate LazyPromise
87 (#121 by @holtkamp)
88
89 * Follower cancellation propagation was originally planned for this release
90 but has been reverted for now and is planned for a future release.
91 (#99 by @jsor and #122 by @clue)
92
93 * 2.5.1 (2017-03-25)
94
95 * Fix circular references when resolving with a promise which follows
96 itself (#94).
97
98 * 2.5.0 (2016-12-22)
99
100 * Revert automatic cancellation of pending collection promises once the
101 output promise resolves. This was introduced in 42d86b7 (PR #36, released
102 in [v2.3.0](https://github.com/reactphp/promise/releases/tag/v2.3.0)) and
103 was both unintended and backward incompatible.
104
105 If you need automatic cancellation, you can use something like:
21 Please note, that the following code (which has been commonly used to
22 conditionally cancel a promise) is not longer possible:
10623
10724 ```php
108 function allAndCancel(array $promises)
109 {
110 return \React\Promise\all($promises)
111 ->always(function() use ($promises) {
112 foreach ($promises as $promise) {
113 if ($promise instanceof \React\Promise\CancellablePromiseInterface) {
114 $promise->cancel();
115 }
116 }
117 });
25 if ($promise instanceof CancellablePromiseInterface) {
26 $promise->cancel();
11827 }
11928 ```
120 * `all()` and `map()` functions now preserve the order of the array (#77).
121 * Fix circular references when resolving a promise with itself (#71).
12229
123 * 2.4.1 (2016-05-03)
30 If only supporting react/promise >= 3.0, it can be simply changed to:
12431
125 * Fix `some()` not cancelling pending promises when too much input promises
126 reject (16ff799).
32 ```php
33 if ($promise instanceof PromiseInterface) {
34 $promise->cancel();
35 }
36 ```
12737
128 * 2.4.0 (2016-03-31)
38 If also react/promise < 3.0 must be supported, the following code can be
39 used:
12940
130 * Support foreign thenables in `resolve()`.
131 Any object that provides a `then()` method is now assimilated to a trusted
132 promise that follows the state of this thenable (#52).
133 * Fix `some()` and `any()` for input arrays containing not enough items
134 (#34).
135
136 * 2.3.0 (2016-03-24)
137
138 * Allow cancellation of promises returned by functions working on promise
139 collections (#36).
140 * Handle `\Throwable` in the same way as `\Exception` (#51 by @joshdifabio).
141
142 * 2.2.2 (2016-02-26)
143
144 * Fix cancellation handlers called multiple times (#47 by @clue).
145
146 * 2.2.1 (2015-07-03)
147
148 * Fix stack error when resolving a promise in its own fulfillment or
149 rejection handlers.
150
151 * 2.2.0 (2014-12-30)
152
153 * Introduce new `ExtendedPromiseInterface` implemented by all promises.
154 * Add new `done()` method (part of the `ExtendedPromiseInterface`).
155 * Add new `otherwise()` method (part of the `ExtendedPromiseInterface`).
156 * Add new `always()` method (part of the `ExtendedPromiseInterface`).
157 * Add new `progress()` method (part of the `ExtendedPromiseInterface`).
158 * Rename `Deferred::progress` to `Deferred::notify` to avoid confusion with
159 `ExtendedPromiseInterface::progress` (a `Deferred::progress` alias is
160 still available for backward compatibility)
161 * `resolve()` now always returns a `ExtendedPromiseInterface`.
162
163 * 2.1.0 (2014-10-15)
164
165 * Introduce new `CancellablePromiseInterface` implemented by all promises.
166 * Add new `cancel()` method (part of the `CancellablePromiseInterface`).
167
168 * 2.0.0 (2013-12-10)
169
170 New major release. The goal is to streamline the API and to make it more
171 compliant with other promise libraries and especially with the new upcoming
172 [ES6 promises specification](https://github.com/domenic/promises-unwrapping/).
173
174 * Add standalone Promise class.
175 * Add new `race()` function.
176 * BC break: Bump minimum PHP version to PHP 5.4.
177 * BC break: Remove `ResolverInterface` and `PromiseInterface` from
178 `Deferred`.
179 * BC break: Change signature of `PromiseInterface`.
180 * BC break: Remove `When` and `Util` classes and move static methods to
181 functions.
182 * BC break: `FulfilledPromise` and `RejectedPromise` now throw an exception
183 when initialized with a promise instead of a value/reason.
184 * BC break: `Deferred::resolve()` and `Deferred::reject()` no longer return
185 a promise.
41 ```php
42 if ($promise instanceof PromiseInterface) {
43 \React\Promise\resolve($promise)->cancel();
44 }
45 ```
46 * BC break: When rejecting a promise, a `\Throwable` or `\Exception`
47 instance is enforced as the rejection reason (#93).
48 This means, it is not longer possible to reject a promise without a reason
49 or with another promise.
33 A lightweight implementation of
44 [CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP.
55
6 [![CI status](https://github.com/reactphp/promise/workflows/CI/badge.svg?branch=2.x)](https://github.com/reactphp/promise/actions)
6 [![CI status](https://github.com/reactphp/promise/workflows/CI/badge.svg)](https://github.com/reactphp/promise/actions)
7 [![installs on Packagist](https://img.shields.io/packagist/dt/react/promise?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/react/promise)
8
9 > **Development version:** This branch contains the code for the upcoming 3.0 release.
10 > For the code of the current stable 2.x release, check out the
11 > [`2.x` branch](https://github.com/reactphp/promise/tree/2.x).
12 >
13 > The upcoming 3.0 release will be the way forward for this package.
14 > However, we will still actively support 2.0 and 1.0 for those not yet
15 > on PHP 7.1+.
716
817 Table of Contents
918 -----------------
1726 * [Deferred::promise()](#deferredpromise)
1827 * [Deferred::resolve()](#deferredresolve)
1928 * [Deferred::reject()](#deferredreject)
20 * [Deferred::notify()](#deferrednotify)
2129 * [PromiseInterface](#promiseinterface)
2230 * [PromiseInterface::then()](#promiseinterfacethen)
23 * [ExtendedPromiseInterface](#extendedpromiseinterface)
24 * [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone)
25 * [ExtendedPromiseInterface::otherwise()](#extendedpromiseinterfaceotherwise)
26 * [ExtendedPromiseInterface::always()](#extendedpromiseinterfacealways)
27 * [ExtendedPromiseInterface::progress()](#extendedpromiseinterfaceprogress)
28 * [CancellablePromiseInterface](#cancellablepromiseinterface)
29 * [CancellablePromiseInterface::cancel()](#cancellablepromiseinterfacecancel)
31 * [PromiseInterface::catch()](#promiseinterfacecatch)
32 * [PromiseInterface::finally()](#promiseinterfacefinally)
33 * [PromiseInterface::cancel()](#promiseinterfacecancel)
34 * [~~PromiseInterface::otherwise()~~](#promiseinterfaceotherwise)
35 * [~~PromiseInterface::always()~~](#promiseinterfacealways)
3036 * [Promise](#promise-2)
31 * [FulfilledPromise](#fulfilledpromise)
32 * [RejectedPromise](#rejectedpromise)
33 * [LazyPromise](#lazypromise)
3437 * [Functions](#functions)
3538 * [resolve()](#resolve)
3639 * [reject()](#reject)
3740 * [all()](#all)
3841 * [race()](#race)
3942 * [any()](#any)
40 * [some()](#some)
41 * [map()](#map)
42 * [reduce()](#reduce)
43 * [PromisorInterface](#promisorinterface)
4443 4. [Examples](#examples)
4544 * [How to use Deferred](#how-to-use-deferred)
4645 * [How promise forwarding works](#how-promise-forwarding-works)
4746 * [Resolution forwarding](#resolution-forwarding)
4847 * [Rejection forwarding](#rejection-forwarding)
4948 * [Mixed resolution and rejection forwarding](#mixed-resolution-and-rejection-forwarding)
50 * [Progress event forwarding](#progress-event-forwarding)
51 * [done() vs. then()](#done-vs-then)
5249 5. [Install](#install)
5350 6. [Credits](#credits)
5451 7. [License](#license)
9390
9491 $promise = $deferred->promise();
9592
96 $deferred->resolve(mixed $value = null);
97 $deferred->reject(mixed $reason = null);
98 $deferred->notify(mixed $update = null);
93 $deferred->resolve(mixed $value);
94 $deferred->reject(\Throwable $reason);
9995 ```
10096
10197 The `promise` method returns the promise of the deferred.
10298
10399 The `resolve` and `reject` methods control the state of the deferred.
104
105 The deprecated `notify` method is for progress notification.
106100
107101 The constructor of the `Deferred` accepts an optional `$canceller` argument.
108102 See [Promise](#promise-2) for more information.
119113 #### Deferred::resolve()
120114
121115 ```php
122 $deferred->resolve(mixed $value = null);
116 $deferred->resolve(mixed $value);
123117 ```
124118
125119 Resolves the promise returned by `promise()`. All consumers are notified by
132126 #### Deferred::reject()
133127
134128 ```php
135 $deferred->reject(mixed $reason = null);
129 $deferred->reject(\Throwable $reason);
136130 ```
137131
138132 Rejects the promise returned by `promise()`, signalling that the deferred's
140134 All consumers are notified by having `$onRejected` (which they registered via
141135 `$promise->then()`) called with `$reason`.
142136
143 If `$reason` itself is a promise, the promise will be rejected with the outcome
144 of this promise regardless whether it fulfills or rejects.
145
146 #### Deferred::notify()
147
148 > Deprecated in v2.6.0: Progress support is deprecated and should not be used anymore.
149
150 ```php
151 $deferred->notify(mixed $update = null);
152 ```
153
154 Triggers progress notifications, to indicate to consumers that the computation
155 is making progress toward its result.
156
157 All consumers are notified by having `$onProgress` (which they registered via
158 `$promise->then()`) called with `$update`.
159
160137 ### PromiseInterface
161138
162139 The promise interface provides the common interface for all promise
163140 implementations.
141 See [Promise](#promise-2) for the only public implementation exposed by this
142 package.
164143
165144 A promise represents an eventual outcome, which is either fulfillment (success)
166145 and an associated value, or rejection (failure) and an associated reason.
168147 Once in the fulfilled or rejected state, a promise becomes immutable.
169148 Neither its state nor its result (or error) can be modified.
170149
171 #### Implementations
172
173 * [Promise](#promise-2)
174 * [FulfilledPromise](#fulfilledpromise) (deprecated)
175 * [RejectedPromise](#rejectedpromise) (deprecated)
176 * [LazyPromise](#lazypromise) (deprecated)
177
178150 #### PromiseInterface::then()
179151
180152 ```php
181 $transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null);
153 $transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null);
182154 ```
183155
184156 Transforms a promise's value by applying a function to the promise's fulfillment
185157 or rejection value. Returns a new promise for the transformed result.
186158
187 The `then()` method registers new fulfilled, rejection and progress handlers
188 with a promise (all parameters are optional):
159 The `then()` method registers new fulfilled and rejection handlers with a promise
160 (all parameters are optional):
189161
190162 * `$onFulfilled` will be invoked once the promise is fulfilled and passed
191163 the result as the first argument.
192164 * `$onRejected` will be invoked once the promise is rejected and passed the
193165 reason as the first argument.
194 * `$onProgress` (deprecated) will be invoked whenever the producer of the promise
195 triggers progress notifications and passed a single argument (whatever it
196 wants) to indicate progress.
197166
198167 It returns a new promise that will fulfill with the return value of either
199168 `$onFulfilled` or `$onRejected`, whichever is called, or will reject with
206175 never both.
207176 2. `$onFulfilled` and `$onRejected` will never be called more
208177 than once.
209 3. `$onProgress` (deprecated) may be called multiple times.
210178
211179 #### See also
212180
213181 * [resolve()](#resolve) - Creating a resolved promise
214182 * [reject()](#reject) - Creating a rejected promise
215 * [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone)
216 * [done() vs. then()](#done-vs-then)
217
218 ### ExtendedPromiseInterface
219
220 The ExtendedPromiseInterface extends the PromiseInterface with useful shortcut
221 and utility methods which are not part of the Promises/A specification.
222
223 #### Implementations
224
225 * [Promise](#promise-1)
226 * [FulfilledPromise](#fulfilledpromise) (deprecated)
227 * [RejectedPromise](#rejectedpromise) (deprecated)
228 * [LazyPromise](#lazypromise) (deprecated)
229
230 #### ExtendedPromiseInterface::done()
231
232 ```php
233 $promise->done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null);
234 ```
235
236 Consumes the promise's ultimate value if the promise fulfills, or handles the
237 ultimate error.
238
239 It will cause a fatal error if either `$onFulfilled` or `$onRejected` throw or
240 return a rejected promise.
241
242 Since the purpose of `done()` is consumption rather than transformation,
243 `done()` always returns `null`.
244
245 #### See also
246
247 * [PromiseInterface::then()](#promiseinterfacethen)
248 * [done() vs. then()](#done-vs-then)
249
250 #### ExtendedPromiseInterface::otherwise()
251
252 ```php
253 $promise->otherwise(callable $onRejected);
183
184 #### PromiseInterface::catch()
185
186 ```php
187 $promise->catch(callable $onRejected);
254188 ```
255189
256190 Registers a rejection handler for promise. It is a shortcut for:
264198
265199 ```php
266200 $promise
267 ->otherwise(function (\RuntimeException $reason) {
201 ->catch(function (\RuntimeException $reason) {
268202 // Only catch \RuntimeException instances
269203 // All other types of errors will propagate automatically
270204 })
271 ->otherwise(function ($reason) {
205 ->catch(function (\Throwable $reason) {
272206 // Catch other errors
273 )};
274 ```
275
276 #### ExtendedPromiseInterface::always()
277
278 ```php
279 $newPromise = $promise->always(callable $onFulfilledOrRejected);
207 });
208 ```
209
210 #### PromiseInterface::finally()
211
212 ```php
213 $newPromise = $promise->finally(callable $onFulfilledOrRejected);
280214 ```
281215
282216 Allows you to execute "cleanup" type tasks in a promise chain.
295229 rejected promise, `$newPromise` will reject with the thrown exception or
296230 rejected promise's reason.
297231
298 `always()` behaves similarly to the synchronous finally statement. When combined
299 with `otherwise()`, `always()` allows you to write code that is similar to the familiar
232 `finally()` behaves similarly to the synchronous finally statement. When combined
233 with `catch()`, `finally()` allows you to write code that is similar to the familiar
300234 synchronous catch/finally pair.
301235
302236 Consider the following synchronous code:
303237
304238 ```php
305239 try {
306 return doSomething();
307 } catch(\Exception $e) {
240 return doSomething();
241 } catch (\Throwable $e) {
308242 return handleError($e);
309243 } finally {
310244 cleanup();
316250
317251 ```php
318252 return doSomething()
319 ->otherwise('handleError')
320 ->always('cleanup');
321 ```
322
323 #### ExtendedPromiseInterface::progress()
324
325 > Deprecated in v2.6.0: Progress support is deprecated and should not be used anymore.
326
327 ```php
328 $promise->progress(callable $onProgress);
329 ```
330
331 Registers a handler for progress updates from promise. It is a shortcut for:
332
333 ```php
334 $promise->then(null, null, $onProgress);
335 ```
336
337 ### CancellablePromiseInterface
338
339 A cancellable promise provides a mechanism for consumers to notify the creator
340 of the promise that they are not longer interested in the result of an
341 operation.
342
343 #### CancellablePromiseInterface::cancel()
253 ->catch('handleError')
254 ->finally('cleanup');
255 ```
256
257 #### PromiseInterface::cancel()
344258
345259 ``` php
346260 $promise->cancel();
352266 Once a promise is settled (either fulfilled or rejected), calling `cancel()` on
353267 a promise has no effect.
354268
355 #### Implementations
356
357 * [Promise](#promise-1)
358 * [FulfilledPromise](#fulfilledpromise) (deprecated)
359 * [RejectedPromise](#rejectedpromise) (deprecated)
360 * [LazyPromise](#lazypromise) (deprecated)
269 #### ~~PromiseInterface::otherwise()~~
270
271 > Deprecated since v3.0.0, see [`catch()`](#promiseinterfacecatch) instead.
272
273 The `otherwise()` method registers a rejection handler for a promise.
274
275 This method continues to exist only for BC reasons and to ease upgrading
276 between versions. It is an alias for:
277
278 ```php
279 $promise->catch($onRejected);
280 ```
281
282 #### ~~PromiseInterface::always()~~
283
284 > Deprecated since v3.0.0, see [`finally()`](#promiseinterfacefinally) instead.
285
286 The `always()` method allows you to execute "cleanup" type tasks in a promise chain.
287
288 This method continues to exist only for BC reasons and to ease upgrading
289 between versions. It is an alias for:
290
291 ```php
292 $promise->finally($onFulfilledOrRejected);
293 ```
361294
362295 ### Promise
363296
365298 `$resolver`.
366299
367300 ```php
368 $resolver = function (callable $resolve, callable $reject, callable $notify) {
301 $resolver = function (callable $resolve, callable $reject) {
369302 // Do some work, possibly asynchronously, and then
370 // resolve or reject. You can notify of progress events (deprecated)
371 // along the way if you want/need.
303 // resolve or reject.
372304
373305 $resolve($awesomeResult);
374306 // or throw new Exception('Promise rejected');
375307 // or $resolve($anotherPromise);
376308 // or $reject($nastyError);
377 // or $notify($progressNotification);
378309 };
379310
380311 $canceller = function () {
397328 fate will be equivalent to that of `$otherPromise`.
398329 * `$reject($reason)` - Function that rejects the promise. It is recommended to
399330 just throw an exception instead of using `$reject()`.
400 * `$notify($update)` - Deprecated function that issues progress events for the promise.
401331
402332 If the resolver or canceller throw an exception, the promise will be rejected
403333 with that thrown exception as the rejection reason.
405335 The resolver function will be called immediately, the canceller function only
406336 once all consumers called the `cancel()` method of the promise.
407337
408 ### FulfilledPromise
409
410 > Deprecated in v2.8.0: External usage of `FulfilledPromise` is deprecated, use `resolve()` instead.
411
412 Creates a already fulfilled promise.
413
414 ```php
415 $promise = React\Promise\FulfilledPromise($value);
416 ```
417
418 Note, that `$value` **cannot** be a promise. It's recommended to use
419 [resolve()](#resolve) for creating resolved promises.
420
421 ### RejectedPromise
422
423 > Deprecated in v2.8.0: External usage of `RejectedPromise` is deprecated, use `reject()` instead.
424
425 Creates a already rejected promise.
426
427 ```php
428 $promise = React\Promise\RejectedPromise($reason);
429 ```
430
431 Note, that `$reason` **cannot** be a promise. It's recommended to use
432 [reject()](#reject) for creating rejected promises.
433
434 ### LazyPromise
435
436 > Deprecated in v2.8.0: LazyPromise is deprecated and should not be used anymore.
437
438 Creates a promise which will be lazily initialized by `$factory` once a consumer
439 calls the `then()` method.
440
441 ```php
442 $factory = function () {
443 $deferred = new React\Promise\Deferred();
444
445 // Do some heavy stuff here and resolve the deferred once completed
446
447 return $deferred->promise();
448 };
449
450 $promise = new React\Promise\LazyPromise($factory);
451
452 // $factory will only be executed once we call then()
453 $promise->then(function ($value) {
454 });
455 ```
456
457338 ### Functions
458339
459 Useful functions for creating, joining, mapping and reducing collections of
460 promises.
461
462 All functions working on promise collections (like `all()`, `race()`, `some()`
340 Useful functions for creating and joining collections of promises.
341
342 All functions working on promise collections (like `all()`, `race()`,
463343 etc.) support cancellation. This means, if you call `cancel()` on the returned
464 promise, all promises in the collection are cancelled. If the collection itself
465 is a promise which resolves to an array, this promise is also cancelled.
344 promise, all promises in the collection are cancelled.
466345
467346 #### resolve()
468347
480359
481360 If `$promiseOrValue` is a promise, it will be returned as is.
482361
483 Note: The promise returned is always a promise implementing
484 [ExtendedPromiseInterface](#extendedpromiseinterface). If you pass in a custom
485 promise which only implements [PromiseInterface](#promiseinterface), this
486 promise will be assimilated to a extended promise following `$promiseOrValue`.
487
488362 #### reject()
489363
490364 ```php
491 $promise = React\Promise\reject(mixed $promiseOrValue);
492 ```
493
494 Creates a rejected promise for the supplied `$promiseOrValue`.
495
496 If `$promiseOrValue` is a value, it will be the rejection value of the
497 returned promise.
498
499 If `$promiseOrValue` is a promise, its completion value will be the rejected
500 value of the returned promise.
501
502 This can be useful in situations where you need to reject a promise without
503 throwing an exception. For example, it allows you to propagate a rejection with
504 the value of another promise.
365 $promise = React\Promise\reject(\Throwable $reason);
366 ```
367
368 Creates a rejected promise for the supplied `$reason`.
369
370 Note that the [`\Throwable`](https://www.php.net/manual/en/class.throwable.php) interface introduced in PHP 7 covers
371 both user land [`\Exception`](https://www.php.net/manual/en/class.exception.php)'s and
372 [`\Error`](https://www.php.net/manual/en/class.error.php) internal PHP errors. By enforcing `\Throwable` as reason to
373 reject a promise, any language error or user land exception can be used to reject a promise.
505374
506375 #### all()
507376
508377 ```php
509 $promise = React\Promise\all(array|React\Promise\PromiseInterface $promisesOrValues);
378 $promise = React\Promise\all(iterable $promisesOrValues);
510379 ```
511380
512381 Returns a promise that will resolve only once all the items in
517386 #### race()
518387
519388 ```php
520 $promise = React\Promise\race(array|React\Promise\PromiseInterface $promisesOrValues);
389 $promise = React\Promise\race(iterable $promisesOrValues);
521390 ```
522391
523392 Initiates a competitive race that allows one winner. Returns a promise which is
524393 resolved in the same way the first settled promise resolves.
525394
395 The returned promise will become **infinitely pending** if `$promisesOrValues`
396 contains 0 items.
397
526398 #### any()
527399
528400 ```php
529 $promise = React\Promise\any(array|React\Promise\PromiseInterface $promisesOrValues);
401 $promise = React\Promise\any(iterable $promisesOrValues);
530402 ```
531403
532404 Returns a promise that will resolve when any one of the items in
534406 will be the resolution value of the triggering item.
535407
536408 The returned promise will only reject if *all* items in `$promisesOrValues` are
537 rejected. The rejection value will be an array of all rejection reasons.
409 rejected. The rejection value will be a `React\Promise\Exception\CompositeException`
410 which holds all rejection reasons. The rejection reasons can be obtained with
411 `CompositeException::getThrowables()`.
538412
539413 The returned promise will also reject with a `React\Promise\Exception\LengthException`
540414 if `$promisesOrValues` contains 0 items.
541
542 #### some()
543
544 ```php
545 $promise = React\Promise\some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany);
546 ```
547
548 Returns a promise that will resolve when `$howMany` of the supplied items in
549 `$promisesOrValues` resolve. The resolution value of the returned promise
550 will be an array of length `$howMany` containing the resolution values of the
551 triggering items.
552
553 The returned promise will reject if it becomes impossible for `$howMany` items
554 to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items
555 reject). The rejection value will be an array of
556 `(count($promisesOrValues) - $howMany) + 1` rejection reasons.
557
558 The returned promise will also reject with a `React\Promise\Exception\LengthException`
559 if `$promisesOrValues` contains less items than `$howMany`.
560
561 #### map()
562
563 ```php
564 $promise = React\Promise\map(array|React\Promise\PromiseInterface $promisesOrValues, callable $mapFunc);
565 ```
566
567 Traditional map function, similar to `array_map()`, but allows input to contain
568 promises and/or values, and `$mapFunc` may return either a value or a promise.
569
570 The map function receives each item as argument, where item is a fully resolved
571 value of a promise or value in `$promisesOrValues`.
572
573 #### reduce()
574
575 ```php
576 $promise = React\Promise\reduce(array|React\Promise\PromiseInterface $promisesOrValues, callable $reduceFunc , $initialValue = null);
577 ```
578
579 Traditional reduce function, similar to `array_reduce()`, but input may contain
580 promises and/or values, and `$reduceFunc` may return either a value or a
581 promise, *and* `$initialValue` may be a promise or a value for the starting
582 value.
583
584 ### PromisorInterface
585
586 The `React\Promise\PromisorInterface` provides a common interface for objects
587 that provide a promise. `React\Promise\Deferred` implements it, but since it
588 is part of the public API anyone can implement it.
589415
590416 Examples
591417 --------
598424 $deferred = new React\Promise\Deferred();
599425
600426 // Execute a Node.js-style function using the callback pattern
601 computeAwesomeResultAsynchronously(function ($error, $result) use ($deferred) {
427 computeAwesomeResultAsynchronously(function (\Throwable $error, $result) use ($deferred) {
602428 if ($error) {
603429 $deferred->reject($error);
604430 } else {
615441 function ($value) {
616442 // Deferred resolved, do something with $value
617443 },
618 function ($reason) {
444 function (\Throwable $reason) {
619445 // Deferred rejected, do something with $reason
620 },
621 function ($update) {
622 // Progress notification triggered, do something with $update
623446 }
624447 );
625448 ```
687510 ->then(function ($x) {
688511 throw new \Exception($x + 1);
689512 })
690 ->otherwise(function (\Exception $x) {
513 ->catch(function (\Exception $x) {
691514 // Propagate the rejection
692515 throw $x;
693516 })
694 ->otherwise(function (\Exception $x) {
517 ->catch(function (\Exception $x) {
695518 // Can also propagate by returning another rejection
696519 return React\Promise\reject(
697520 new \Exception($x->getMessage() + 1)
698521 );
699522 })
700 ->otherwise(function ($x) {
523 ->catch(function ($x) {
701524 echo 'Reject ' . $x->getMessage(); // 3
702525 });
703526
719542 ->then(function ($x) {
720543 throw new \Exception($x + 1);
721544 })
722 ->otherwise(function (\Exception $x) {
545 ->catch(function (\Exception $x) {
723546 // Handle the rejection, and don't propagate.
724547 // This is like catch without a rethrow
725548 return $x->getMessage() + 1;
731554 $deferred->resolve(1); // Prints "Mixed 4"
732555 ```
733556
734 #### Progress event forwarding
735
736 > Deprecated in v2.6.0: Progress support is deprecated and should not be used anymore.
737
738 In the same way as resolution and rejection handlers, your progress handler
739 **MUST** return a progress event to be propagated to the next link in the chain.
740 If you return nothing, `null` will be propagated.
741
742 Also in the same way as resolutions and rejections, if you don't register a
743 progress handler, the update will be propagated through.
744
745 If your progress handler throws an exception, the exception will be propagated
746 to the next link in the chain. The best thing to do is to ensure your progress
747 handlers do not throw exceptions.
748
749 This gives you the opportunity to transform progress events at each step in the
750 chain so that they are meaningful to the next step. It also allows you to choose
751 not to transform them, and simply let them propagate untransformed, by not
752 registering a progress handler.
753
754 ```php
755 $deferred = new React\Promise\Deferred();
756
757 $deferred->promise()
758 ->progress(function ($update) {
759 return $update + 1;
760 })
761 ->progress(function ($update) {
762 echo 'Progress ' . $update; // 2
763 });
764
765 $deferred->notify(1); // Prints "Progress 2"
766 ```
767
768 ### done() vs. then()
769
770 The golden rule is:
771
772 Either return your promise, or call done() on it.
773
774 At a first glance, `then()` and `done()` seem very similar. However, there are
775 important distinctions.
776
777 The intent of `then()` is to transform a promise's value and to pass or return
778 a new promise for the transformed value along to other parts of your code.
779
780 The intent of `done()` is to consume a promise's value, transferring
781 responsibility for the value to your code.
782
783 In addition to transforming a value, `then()` allows you to recover from, or
784 propagate intermediate errors. Any errors that are not handled will be caught
785 by the promise machinery and used to reject the promise returned by `then()`.
786
787 Calling `done()` transfers all responsibility for errors to your code. If an
788 error (either a thrown exception or returned rejection) escapes the
789 `$onFulfilled` or `$onRejected` callbacks you provide to done, it will be
790 rethrown in an uncatchable way causing a fatal error.
791
792 ```php
793 function getJsonResult()
794 {
795 return queryApi()
796 ->then(
797 // Transform API results to an object
798 function ($jsonResultString) {
799 return json_decode($jsonResultString);
800 },
801 // Transform API errors to an exception
802 function ($jsonErrorString) {
803 $object = json_decode($jsonErrorString);
804 throw new ApiErrorException($object->errorMessage);
805 }
806 );
807 }
808
809 // Here we provide no rejection handler. If the promise returned has been
810 // rejected, the ApiErrorException will be thrown
811 getJsonResult()
812 ->done(
813 // Consume transformed object
814 function ($jsonResultObject) {
815 // Do something with $jsonResultObject
816 }
817 );
818
819 // Here we provide a rejection handler which will either throw while debugging
820 // or log the exception
821 getJsonResult()
822 ->done(
823 function ($jsonResultObject) {
824 // Do something with $jsonResultObject
825 },
826 function (ApiErrorException $exception) {
827 if (isDebug()) {
828 throw $exception;
829 } else {
830 logException($exception);
831 }
832 }
833 );
834 ```
835
836 Note that if a rejection value is not an instance of `\Exception`, it will be
837 wrapped in an exception of the type `React\Promise\UnhandledRejectionException`.
838
839 You can get the original rejection reason by calling `$exception->getReason()`.
840
841557 Install
842558 -------
843559
844 The recommended way to install this library is [through Composer](https://getcomposer.org).
560 The recommended way to install this library is [through Composer](https://getcomposer.org/).
845561 [New to Composer?](https://getcomposer.org/doc/00-intro.md)
846562
847 This project follows [SemVer](https://semver.org/).
848 This will install the latest supported version:
563 Once released, this project will follow [SemVer](https://semver.org/).
564 At the moment, this will install the latest development version:
849565
850566 ```bash
851 $ composer require react/promise:^2.9
567 composer require react/promise:^3@dev
852568 ```
853569
854570 See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
855571
856572 This project aims to run on any platform and thus does not require any PHP
857 extensions and supports running on legacy PHP 5.4 through current PHP 8+ and HHVM.
573 extensions and supports running on PHP 7.1 through current PHP 8+.
858574 It's *highly recommended to use the latest supported PHP version* for this project.
575
576 We're committed to providing long-term support (LTS) options and to provide a
577 smooth upgrade path. If you're using an older PHP version, you may use the
578 [`2.x` branch](https://github.com/reactphp/promise/tree/2.x) (PHP 5.4+) or
579 [`1.x` branch](https://github.com/reactphp/promise/tree/1.x) (PHP 5.3+) which both
580 provide a compatible API but do not take advantage of newer language features.
581 You may target multiple versions at the same time to support a wider range of
582 PHP versions like this:
583
584 ```bash
585 composer require "react/promise:^3@dev || ^2 || ^1"
586 ```
859587
860588 Credits
861589 -------
2424 }
2525 ],
2626 "require": {
27 "php": ">=5.4.0"
27 "php": ">=7.1.0"
2828 },
2929 "require-dev": {
30 "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36"
30 "phpunit/phpunit": "^9.5 || ^7.5"
3131 },
3232 "autoload": {
3333 "psr-4": {
3737 },
3838 "autoload-dev": {
3939 "psr-4": {
40 "React\\Promise\\": ["tests", "tests/fixtures"]
40 "React\\Promise\\": [
41 "tests/fixtures/",
42 "tests/"
43 ]
4144 }
4245 },
4346 "keywords": [
00 <?xml version="1.0" encoding="UTF-8"?>
11
2 <!-- PHPUnit configuration file with new format for PHPUnit 9.3+ -->
3 <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
2 <!-- PHPUnit configuration file with new format for PHPUnit 9.5+ -->
3 <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
55 bootstrap="vendor/autoload.php"
6 cacheResult="false"
67 colors="true"
7 cacheResult="false">
8 convertDeprecationsToExceptions="true">
89 <testsuites>
910 <testsuite name="Promise Test Suite">
1011 <directory>./tests/</directory>
1819 <file>./src/functions_include.php</file>
1920 </exclude>
2021 </coverage>
22 <php>
23 <ini name="error_reporting" value="-1" />
24 <!-- Evaluate assertions, requires running with "php -d zend.assertions=1 vendor/bin/phpunit" -->
25 <!-- <ini name="zend.assertions=1" value="1" /> -->
26 <ini name="assert.active" value="1" />
27 <ini name="assert.exception" value="1" />
28 <ini name="assert.bail" value="0" />
29 </php>
2130 </phpunit>
00 <?xml version="1.0" encoding="UTF-8"?>
11
2 <!-- PHPUnit configuration file with old format for PHPUnit 9.2 or older -->
2 <!-- PHPUnit configuration file with old format before PHPUnit 9 -->
33 <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/4.8/phpunit.xsd"
4 xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
55 bootstrap="vendor/autoload.php"
66 colors="true">
77 <testsuites>
1717 </exclude>
1818 </whitelist>
1919 </filter>
20 <php>
21 <ini name="error_reporting" value="-1" />
22 <!-- Evaluate assertions, requires running with "php -d zend.assertions=1 vendor/bin/phpunit" -->
23 <!-- <ini name="zend.assertions=1" value="1" /> -->
24 <ini name="assert.active" value="1" />
25 <ini name="assert.exception" value="1" />
26 <ini name="assert.bail" value="0" />
27 </php>
2028 </phpunit>
+0
-17
src/CancellablePromiseInterface.php less more
0 <?php
1
2 namespace React\Promise;
3
4 interface CancellablePromiseInterface extends PromiseInterface
5 {
6 /**
7 * The `cancel()` method notifies the creator of the promise that there is no
8 * further interest in the results of the operation.
9 *
10 * Once a promise is settled (either fulfilled or rejected), calling `cancel()` on
11 * a promise has no effect.
12 *
13 * @return void
14 */
15 public function cancel();
16 }
+0
-55
src/CancellationQueue.php less more
0 <?php
1
2 namespace React\Promise;
3
4 class CancellationQueue
5 {
6 private $started = false;
7 private $queue = [];
8
9 public function __invoke()
10 {
11 if ($this->started) {
12 return;
13 }
14
15 $this->started = true;
16 $this->drain();
17 }
18
19 public function enqueue($cancellable)
20 {
21 if (!\is_object($cancellable) || !\method_exists($cancellable, 'then') || !\method_exists($cancellable, 'cancel')) {
22 return;
23 }
24
25 $length = \array_push($this->queue, $cancellable);
26
27 if ($this->started && 1 === $length) {
28 $this->drain();
29 }
30 }
31
32 private function drain()
33 {
34 for ($i = key($this->queue); isset($this->queue[$i]); $i++) {
35 $cancellable = $this->queue[$i];
36
37 $exception = null;
38
39 try {
40 $cancellable->cancel();
41 } catch (\Throwable $exception) {
42 } catch (\Exception $exception) {
43 }
44
45 unset($this->queue[$i]);
46
47 if ($exception) {
48 throw $exception;
49 }
50 }
51
52 $this->queue = [];
53 }
54 }
11
22 namespace React\Promise;
33
4 class Deferred implements PromisorInterface
4 final class Deferred
55 {
66 private $promise;
77 private $resolveCallback;
88 private $rejectCallback;
9 private $notifyCallback;
10 private $canceller;
119
1210 public function __construct(callable $canceller = null)
1311 {
14 $this->canceller = $canceller;
12 $this->promise = new Promise(function ($resolve, $reject): void {
13 $this->resolveCallback = $resolve;
14 $this->rejectCallback = $reject;
15 }, $canceller);
1516 }
1617
17 public function promise()
18 public function promise(): PromiseInterface
1819 {
19 if (null === $this->promise) {
20 $this->promise = new Promise(function ($resolve, $reject, $notify) {
21 $this->resolveCallback = $resolve;
22 $this->rejectCallback = $reject;
23 $this->notifyCallback = $notify;
24 }, $this->canceller);
25 $this->canceller = null;
26 }
27
2820 return $this->promise;
2921 }
3022
31 public function resolve($value = null)
23 public function resolve($value): void
3224 {
33 $this->promise();
34
35 \call_user_func($this->resolveCallback, $value);
25 ($this->resolveCallback)($value);
3626 }
3727
38 public function reject($reason = null)
28 public function reject(\Throwable $reason): void
3929 {
40 $this->promise();
41
42 \call_user_func($this->rejectCallback, $reason);
43 }
44
45 /**
46 * @deprecated 2.6.0 Progress support is deprecated and should not be used anymore.
47 * @param mixed $update
48 */
49 public function notify($update = null)
50 {
51 $this->promise();
52
53 \call_user_func($this->notifyCallback, $update);
54 }
55
56 /**
57 * @deprecated 2.2.0
58 * @see Deferred::notify()
59 */
60 public function progress($update = null)
61 {
62 $this->notify($update);
30 ($this->rejectCallback)($reason);
6331 }
6432 }
0 <?php
1
2 namespace React\Promise\Exception;
3
4 /**
5 * Represents an exception that is a composite of one or more other exceptions.
6 *
7 * This exception is useful in situations where a promise must be rejected
8 * with multiple exceptions. It is used for example to reject the returned
9 * promise from `some()` and `any()` when too many input promises reject.
10 */
11 class CompositeException extends \Exception
12 {
13 private $throwables;
14
15 public function __construct(array $throwables, $message = '', $code = 0, $previous = null)
16 {
17 parent::__construct($message, $code, $previous);
18
19 $this->throwables = $throwables;
20 }
21
22 /**
23 * @return \Throwable[]
24 */
25 public function getThrowables(): array
26 {
27 return $this->throwables;
28 }
29 }
+0
-98
src/ExtendedPromiseInterface.php less more
0 <?php
1
2 namespace React\Promise;
3
4 interface ExtendedPromiseInterface extends PromiseInterface
5 {
6 /**
7 * Consumes the promise's ultimate value if the promise fulfills, or handles the
8 * ultimate error.
9 *
10 * It will cause a fatal error if either `$onFulfilled` or
11 * `$onRejected` throw or return a rejected promise.
12 *
13 * Since the purpose of `done()` is consumption rather than transformation,
14 * `done()` always returns `null`.
15 *
16 * @param callable|null $onFulfilled
17 * @param callable|null $onRejected
18 * @param callable|null $onProgress This argument is deprecated and should not be used anymore.
19 * @return void
20 */
21 public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null);
22
23 /**
24 * Registers a rejection handler for promise. It is a shortcut for:
25 *
26 * ```php
27 * $promise->then(null, $onRejected);
28 * ```
29 *
30 * Additionally, you can type hint the `$reason` argument of `$onRejected` to catch
31 * only specific errors.
32 *
33 * @param callable $onRejected
34 * @return ExtendedPromiseInterface
35 */
36 public function otherwise(callable $onRejected);
37
38 /**
39 * Allows you to execute "cleanup" type tasks in a promise chain.
40 *
41 * It arranges for `$onFulfilledOrRejected` to be called, with no arguments,
42 * when the promise is either fulfilled or rejected.
43 *
44 * * If `$promise` fulfills, and `$onFulfilledOrRejected` returns successfully,
45 * `$newPromise` will fulfill with the same value as `$promise`.
46 * * If `$promise` fulfills, and `$onFulfilledOrRejected` throws or returns a
47 * rejected promise, `$newPromise` will reject with the thrown exception or
48 * rejected promise's reason.
49 * * If `$promise` rejects, and `$onFulfilledOrRejected` returns successfully,
50 * `$newPromise` will reject with the same reason as `$promise`.
51 * * If `$promise` rejects, and `$onFulfilledOrRejected` throws or returns a
52 * rejected promise, `$newPromise` will reject with the thrown exception or
53 * rejected promise's reason.
54 *
55 * `always()` behaves similarly to the synchronous finally statement. When combined
56 * with `otherwise()`, `always()` allows you to write code that is similar to the familiar
57 * synchronous catch/finally pair.
58 *
59 * Consider the following synchronous code:
60 *
61 * ```php
62 * try {
63 * return doSomething();
64 * } catch(\Exception $e) {
65 * return handleError($e);
66 * } finally {
67 * cleanup();
68 * }
69 * ```
70 *
71 * Similar asynchronous code (with `doSomething()` that returns a promise) can be
72 * written:
73 *
74 * ```php
75 * return doSomething()
76 * ->otherwise('handleError')
77 * ->always('cleanup');
78 * ```
79 *
80 * @param callable $onFulfilledOrRejected
81 * @return ExtendedPromiseInterface
82 */
83 public function always(callable $onFulfilledOrRejected);
84
85 /**
86 * Registers a handler for progress updates from promise. It is a shortcut for:
87 *
88 * ```php
89 * $promise->then(null, null, $onProgress);
90 * ```
91 *
92 * @param callable $onProgress
93 * @return ExtendedPromiseInterface
94 * @deprecated 2.6.0 Progress support is deprecated and should not be used anymore.
95 */
96 public function progress(callable $onProgress);
97 }
+0
-71
src/FulfilledPromise.php less more
0 <?php
1
2 namespace React\Promise;
3
4 /**
5 * @deprecated 2.8.0 External usage of FulfilledPromise is deprecated, use `resolve()` instead.
6 */
7 class FulfilledPromise implements ExtendedPromiseInterface, CancellablePromiseInterface
8 {
9 private $value;
10
11 public function __construct($value = null)
12 {
13 if ($value instanceof PromiseInterface) {
14 throw new \InvalidArgumentException('You cannot create React\Promise\FulfilledPromise with a promise. Use React\Promise\resolve($promiseOrValue) instead.');
15 }
16
17 $this->value = $value;
18 }
19
20 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
21 {
22 if (null === $onFulfilled) {
23 return $this;
24 }
25
26 try {
27 return resolve($onFulfilled($this->value));
28 } catch (\Throwable $exception) {
29 return new RejectedPromise($exception);
30 } catch (\Exception $exception) {
31 return new RejectedPromise($exception);
32 }
33 }
34
35 public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
36 {
37 if (null === $onFulfilled) {
38 return;
39 }
40
41 $result = $onFulfilled($this->value);
42
43 if ($result instanceof ExtendedPromiseInterface) {
44 $result->done();
45 }
46 }
47
48 public function otherwise(callable $onRejected)
49 {
50 return $this;
51 }
52
53 public function always(callable $onFulfilledOrRejected)
54 {
55 return $this->then(function ($value) use ($onFulfilledOrRejected) {
56 return resolve($onFulfilledOrRejected())->then(function () use ($value) {
57 return $value;
58 });
59 });
60 }
61
62 public function progress(callable $onProgress)
63 {
64 return $this;
65 }
66
67 public function cancel()
68 {
69 }
70 }
0 <?php
1
2 namespace React\Promise\Internal;
3
4 /**
5 * @internal
6 */
7 final class CancellationQueue
8 {
9 private $started = false;
10 private $queue = [];
11
12 public function __invoke(): void
13 {
14 if ($this->started) {
15 return;
16 }
17
18 $this->started = true;
19 $this->drain();
20 }
21
22 public function enqueue($cancellable): void
23 {
24 if (!\is_object($cancellable) || !\method_exists($cancellable, 'then') || !\method_exists($cancellable, 'cancel')) {
25 return;
26 }
27
28 $length = \array_push($this->queue, $cancellable);
29
30 if ($this->started && 1 === $length) {
31 $this->drain();
32 }
33 }
34
35 private function drain(): void
36 {
37 for ($i = \key($this->queue); isset($this->queue[$i]); $i++) {
38 $cancellable = $this->queue[$i];
39
40 $exception = null;
41
42 try {
43 $cancellable->cancel();
44 } catch (\Throwable $exception) {
45 }
46
47 unset($this->queue[$i]);
48
49 if ($exception) {
50 throw $exception;
51 }
52 }
53
54 $this->queue = [];
55 }
56 }
0 <?php
1
2 namespace React\Promise\Internal;
3
4 use React\Promise\PromiseInterface;
5 use function React\Promise\resolve;
6
7 /**
8 * @internal
9 */
10 final class FulfilledPromise implements PromiseInterface
11 {
12 private $value;
13
14 public function __construct($value = null)
15 {
16 if ($value instanceof PromiseInterface) {
17 throw new \InvalidArgumentException('You cannot create React\Promise\FulfilledPromise with a promise. Use React\Promise\resolve($promiseOrValue) instead.');
18 }
19
20 $this->value = $value;
21 }
22
23 public function then(callable $onFulfilled = null, callable $onRejected = null): PromiseInterface
24 {
25 if (null === $onFulfilled) {
26 return $this;
27 }
28
29 try {
30 return resolve($onFulfilled($this->value));
31 } catch (\Throwable $exception) {
32 return new RejectedPromise($exception);
33 }
34 }
35
36 public function catch(callable $onRejected): PromiseInterface
37 {
38 return $this;
39 }
40
41 public function finally(callable $onFulfilledOrRejected): PromiseInterface
42 {
43 return $this->then(function ($value) use ($onFulfilledOrRejected): PromiseInterface {
44 return resolve($onFulfilledOrRejected())->then(function () use ($value) {
45 return $value;
46 });
47 });
48 }
49
50 public function cancel(): void
51 {
52 }
53
54 /**
55 * @deprecated 3.0.0 Use `catch()` instead
56 * @see self::catch()
57 */
58 public function otherwise(callable $onRejected): PromiseInterface
59 {
60 return $this->catch($onRejected);
61 }
62
63 /**
64 * @deprecated 3.0.0 Use `finally()` instead
65 * @see self::finally()
66 */
67 public function always(callable $onFulfilledOrRejected): PromiseInterface
68 {
69 return $this->finally($onFulfilledOrRejected);
70 }
71 }
0 <?php
1
2 namespace React\Promise\Internal;
3
4 use React\Promise\PromiseInterface;
5 use function React\Promise\_checkTypehint;
6 use function React\Promise\resolve;
7
8 /**
9 * @internal
10 */
11 final class RejectedPromise implements PromiseInterface
12 {
13 private $reason;
14
15 public function __construct(\Throwable $reason)
16 {
17 $this->reason = $reason;
18 }
19
20 public function then(callable $onFulfilled = null, callable $onRejected = null): PromiseInterface
21 {
22 if (null === $onRejected) {
23 return $this;
24 }
25
26 try {
27 return resolve($onRejected($this->reason));
28 } catch (\Throwable $exception) {
29 return new RejectedPromise($exception);
30 }
31 }
32
33 public function catch(callable $onRejected): PromiseInterface
34 {
35 if (!_checkTypehint($onRejected, $this->reason)) {
36 return $this;
37 }
38
39 return $this->then(null, $onRejected);
40 }
41
42 public function finally(callable $onFulfilledOrRejected): PromiseInterface
43 {
44 return $this->then(null, function (\Throwable $reason) use ($onFulfilledOrRejected): PromiseInterface {
45 return resolve($onFulfilledOrRejected())->then(function () use ($reason): PromiseInterface {
46 return new RejectedPromise($reason);
47 });
48 });
49 }
50
51 public function cancel(): void
52 {
53 }
54
55 /**
56 * @deprecated 3.0.0 Use `catch()` instead
57 * @see self::catch()
58 */
59 public function otherwise(callable $onRejected): PromiseInterface
60 {
61 return $this->catch($onRejected);
62 }
63
64 /**
65 * @deprecated 3.0.0 Use `always()` instead
66 * @see self::always()
67 */
68 public function always(callable $onFulfilledOrRejected): PromiseInterface
69 {
70 return $this->finally($onFulfilledOrRejected);
71 }
72 }
+0
-66
src/LazyPromise.php less more
0 <?php
1
2 namespace React\Promise;
3
4 /**
5 * @deprecated 2.8.0 LazyPromise is deprecated and should not be used anymore.
6 */
7 class LazyPromise implements ExtendedPromiseInterface, CancellablePromiseInterface
8 {
9 private $factory;
10 private $promise;
11
12 public function __construct(callable $factory)
13 {
14 $this->factory = $factory;
15 }
16
17 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
18 {
19 return $this->promise()->then($onFulfilled, $onRejected, $onProgress);
20 }
21
22 public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
23 {
24 return $this->promise()->done($onFulfilled, $onRejected, $onProgress);
25 }
26
27 public function otherwise(callable $onRejected)
28 {
29 return $this->promise()->otherwise($onRejected);
30 }
31
32 public function always(callable $onFulfilledOrRejected)
33 {
34 return $this->promise()->always($onFulfilledOrRejected);
35 }
36
37 public function progress(callable $onProgress)
38 {
39 return $this->promise()->progress($onProgress);
40 }
41
42 public function cancel()
43 {
44 return $this->promise()->cancel();
45 }
46
47 /**
48 * @internal
49 * @see Promise::settle()
50 */
51 public function promise()
52 {
53 if (null === $this->promise) {
54 try {
55 $this->promise = resolve(\call_user_func($this->factory));
56 } catch (\Throwable $exception) {
57 $this->promise = new RejectedPromise($exception);
58 } catch (\Exception $exception) {
59 $this->promise = new RejectedPromise($exception);
60 }
61 }
62
63 return $this->promise;
64 }
65 }
11
22 namespace React\Promise;
33
4 class Promise implements ExtendedPromiseInterface, CancellablePromiseInterface
4 use React\Promise\Internal\RejectedPromise;
5
6 final class Promise implements PromiseInterface
57 {
68 private $canceller;
79 private $result;
810
911 private $handlers = [];
10 private $progressHandlers = [];
1112
1213 private $requiredCancelRequests = 0;
13 private $cancelRequests = 0;
1414
1515 public function __construct(callable $resolver, callable $canceller = null)
1616 {
2424 $this->call($cb);
2525 }
2626
27 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
27 public function then(callable $onFulfilled = null, callable $onRejected = null): PromiseInterface
2828 {
2929 if (null !== $this->result) {
30 return $this->result->then($onFulfilled, $onRejected, $onProgress);
30 return $this->result->then($onFulfilled, $onRejected);
3131 }
3232
3333 if (null === $this->canceller) {
34 return new static($this->resolver($onFulfilled, $onRejected, $onProgress));
34 return new static($this->resolver($onFulfilled, $onRejected));
3535 }
3636
3737 // This promise has a canceller, so we create a new child promise which
4343 ++$parent->requiredCancelRequests;
4444
4545 return new static(
46 $this->resolver($onFulfilled, $onRejected, $onProgress),
46 $this->resolver($onFulfilled, $onRejected),
4747 static function () use (&$parent) {
48 if (++$parent->cancelRequests >= $parent->requiredCancelRequests) {
48 --$parent->requiredCancelRequests;
49
50 if ($parent->requiredCancelRequests <= 0) {
4951 $parent->cancel();
5052 }
5153
5456 );
5557 }
5658
57 public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
58 {
59 if (null !== $this->result) {
60 return $this->result->done($onFulfilled, $onRejected, $onProgress);
61 }
62
63 $this->handlers[] = static function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected) {
64 $promise
65 ->done($onFulfilled, $onRejected);
66 };
67
68 if ($onProgress) {
69 $this->progressHandlers[] = $onProgress;
70 }
71 }
72
73 public function otherwise(callable $onRejected)
59 public function catch(callable $onRejected): PromiseInterface
7460 {
7561 return $this->then(null, static function ($reason) use ($onRejected) {
7662 if (!_checkTypehint($onRejected, $reason)) {
8167 });
8268 }
8369
84 public function always(callable $onFulfilledOrRejected)
70 public function finally(callable $onFulfilledOrRejected): PromiseInterface
8571 {
8672 return $this->then(static function ($value) use ($onFulfilledOrRejected) {
8773 return resolve($onFulfilledOrRejected())->then(function () use ($value) {
9480 });
9581 }
9682
97 public function progress(callable $onProgress)
98 {
99 return $this->then(null, null, $onProgress);
100 }
101
102 public function cancel()
103 {
104 if (null === $this->canceller || null !== $this->result) {
105 return;
106 }
107
83 public function cancel(): void
84 {
10885 $canceller = $this->canceller;
10986 $this->canceller = null;
11087
111 $this->call($canceller);
112 }
113
114 private function resolver(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
115 {
116 return function ($resolve, $reject, $notify) use ($onFulfilled, $onRejected, $onProgress) {
117 if ($onProgress) {
118 $progressHandler = static function ($update) use ($notify, $onProgress) {
119 try {
120 $notify($onProgress($update));
121 } catch (\Throwable $e) {
122 $notify($e);
123 } catch (\Exception $e) {
124 $notify($e);
125 }
126 };
127 } else {
128 $progressHandler = $notify;
129 }
130
131 $this->handlers[] = static function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject, $progressHandler) {
132 $promise
133 ->then($onFulfilled, $onRejected)
134 ->done($resolve, $reject, $progressHandler);
88 $parentCanceller = null;
89
90 if (null !== $this->result) {
91 // Go up the promise chain and reach the top most promise which is
92 // itself not following another promise
93 $root = $this->unwrap($this->result);
94
95 // Return if the root promise is already resolved or a
96 // FulfilledPromise or RejectedPromise
97 if (!$root instanceof self || null !== $root->result) {
98 return;
99 }
100
101 $root->requiredCancelRequests--;
102
103 if ($root->requiredCancelRequests <= 0) {
104 $parentCanceller = [$root, 'cancel'];
105 }
106 }
107
108 if (null !== $canceller) {
109 $this->call($canceller);
110 }
111
112 // For BC, we call the parent canceller after our own canceller
113 if ($parentCanceller) {
114 $parentCanceller();
115 }
116 }
117
118 /**
119 * @deprecated 3.0.0 Use `catch()` instead
120 * @see self::catch()
121 */
122 public function otherwise(callable $onRejected): PromiseInterface
123 {
124 return $this->catch($onRejected);
125 }
126
127 /**
128 * @deprecated 3.0.0 Use `finally()` instead
129 * @see self::finally()
130 */
131 public function always(callable $onFulfilledOrRejected): PromiseInterface
132 {
133 return $this->finally($onFulfilledOrRejected);
134 }
135
136 private function resolver(callable $onFulfilled = null, callable $onRejected = null): callable
137 {
138 return function ($resolve, $reject) use ($onFulfilled, $onRejected) {
139 $this->handlers[] = static function (PromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject) {
140 $promise = $promise->then($onFulfilled, $onRejected);
141
142 if ($promise instanceof self && $promise->result === null) {
143 $promise->handlers[] = static function (PromiseInterface $promise) use ($resolve, $reject) {
144 $promise->then($resolve, $reject);
145 };
146 } else {
147 $promise->then($resolve, $reject);
148 }
135149 };
136
137 $this->progressHandlers[] = $progressHandler;
138150 };
139151 }
140152
141 private function reject($reason = null)
153 private function reject(\Throwable $reason): void
142154 {
143155 if (null !== $this->result) {
144156 return;
147159 $this->settle(reject($reason));
148160 }
149161
150 private function settle(ExtendedPromiseInterface $promise)
151 {
152 $promise = $this->unwrap($promise);
153
154 if ($promise === $this) {
155 $promise = new RejectedPromise(
162 private function settle(PromiseInterface $result): void
163 {
164 $result = $this->unwrap($result);
165
166 if ($result === $this) {
167 $result = new RejectedPromise(
156168 new \LogicException('Cannot resolve a promise with itself.')
157169 );
158170 }
159171
172 if ($result instanceof self) {
173 $result->requiredCancelRequests++;
174 } else {
175 // Unset canceller only when not following a pending promise
176 $this->canceller = null;
177 }
178
160179 $handlers = $this->handlers;
161180
162 $this->progressHandlers = $this->handlers = [];
163 $this->result = $promise;
164 $this->canceller = null;
181 $this->handlers = [];
182 $this->result = $result;
165183
166184 foreach ($handlers as $handler) {
167 $handler($promise);
168 }
169 }
170
171 private function unwrap($promise)
172 {
173 $promise = $this->extract($promise);
174
185 $handler($result);
186 }
187 }
188
189 private function unwrap($promise): PromiseInterface
190 {
175191 while ($promise instanceof self && null !== $promise->result) {
176 $promise = $this->extract($promise->result);
192 $promise = $promise->result;
177193 }
178194
179195 return $promise;
180196 }
181197
182 private function extract($promise)
183 {
184 if ($promise instanceof LazyPromise) {
185 $promise = $promise->promise();
186 }
187
188 return $promise;
189 }
190
191 private function call(callable $cb)
198 private function call(callable $cb): void
192199 {
193200 // Explicitly overwrite argument with null value. This ensure that this
194201 // argument does not show up in the stack trace in PHP 7+ only.
222229 // These assumptions are covered by the test suite, so if you ever feel like
223230 // refactoring this, go ahead, any alternative suggestions are welcome!
224231 $target =& $this;
225 $progressHandlers =& $this->progressHandlers;
226232
227233 $callback(
228 static function ($value = null) use (&$target) {
234 static function ($value) use (&$target) {
229235 if ($target !== null) {
230236 $target->settle(resolve($value));
231237 $target = null;
232238 }
233239 },
234 static function ($reason = null) use (&$target) {
240 static function (\Throwable $reason) use (&$target) {
235241 if ($target !== null) {
236242 $target->reject($reason);
237243 $target = null;
238244 }
239 },
240 static function ($update = null) use (&$progressHandlers) {
241 foreach ($progressHandlers as $handler) {
242 $handler($update);
243 }
244245 }
245246 );
246247 }
247248 } catch (\Throwable $e) {
248249 $target = null;
249250 $this->reject($e);
250 } catch (\Exception $e) {
251 $target = null;
252 $this->reject($e);
253251 }
254252 }
255253 }
1414 * the result as the first argument.
1515 * * `$onRejected` will be invoked once the promise is rejected and passed the
1616 * reason as the first argument.
17 * * `$onProgress` (deprecated) will be invoked whenever the producer of the promise
18 * triggers progress notifications and passed a single argument (whatever it
19 * wants) to indicate progress.
2017 *
2118 * It returns a new promise that will fulfill with the return value of either
2219 * `$onFulfilled` or `$onRejected`, whichever is called, or will reject with
2926 * never both.
3027 * 2. `$onFulfilled` and `$onRejected` will never be called more
3128 * than once.
32 * 3. `$onProgress` (deprecated) may be called multiple times.
3329 *
3430 * @param callable|null $onFulfilled
3531 * @param callable|null $onRejected
36 * @param callable|null $onProgress This argument is deprecated and should not be used anymore.
3732 * @return PromiseInterface
3833 */
39 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null);
34 public function then(?callable $onFulfilled = null, ?callable $onRejected = null): PromiseInterface;
35
36 /**
37 * Registers a rejection handler for promise. It is a shortcut for:
38 *
39 * ```php
40 * $promise->then(null, $onRejected);
41 * ```
42 *
43 * Additionally, you can type hint the `$reason` argument of `$onRejected` to catch
44 * only specific errors.
45 *
46 * @param callable $onRejected
47 * @return PromiseInterface
48 */
49 public function catch(callable $onRejected): PromiseInterface;
50
51 /**
52 * Allows you to execute "cleanup" type tasks in a promise chain.
53 *
54 * It arranges for `$onFulfilledOrRejected` to be called, with no arguments,
55 * when the promise is either fulfilled or rejected.
56 *
57 * * If `$promise` fulfills, and `$onFulfilledOrRejected` returns successfully,
58 * `$newPromise` will fulfill with the same value as `$promise`.
59 * * If `$promise` fulfills, and `$onFulfilledOrRejected` throws or returns a
60 * rejected promise, `$newPromise` will reject with the thrown exception or
61 * rejected promise's reason.
62 * * If `$promise` rejects, and `$onFulfilledOrRejected` returns successfully,
63 * `$newPromise` will reject with the same reason as `$promise`.
64 * * If `$promise` rejects, and `$onFulfilledOrRejected` throws or returns a
65 * rejected promise, `$newPromise` will reject with the thrown exception or
66 * rejected promise's reason.
67 *
68 * `finally()` behaves similarly to the synchronous finally statement. When combined
69 * with `catch()`, `finally()` allows you to write code that is similar to the familiar
70 * synchronous catch/finally pair.
71 *
72 * Consider the following synchronous code:
73 *
74 * ```php
75 * try {
76 * return doSomething();
77 * } catch(\Exception $e) {
78 * return handleError($e);
79 * } finally {
80 * cleanup();
81 * }
82 * ```
83 *
84 * Similar asynchronous code (with `doSomething()` that returns a promise) can be
85 * written:
86 *
87 * ```php
88 * return doSomething()
89 * ->catch('handleError')
90 * ->finally('cleanup');
91 * ```
92 *
93 * @param callable $onFulfilledOrRejected
94 * @return PromiseInterface
95 */
96 public function finally(callable $onFulfilledOrRejected): PromiseInterface;
97
98 /**
99 * The `cancel()` method notifies the creator of the promise that there is no
100 * further interest in the results of the operation.
101 *
102 * Once a promise is settled (either fulfilled or rejected), calling `cancel()` on
103 * a promise has no effect.
104 *
105 * @return void
106 */
107 public function cancel(): void;
108
109 /**
110 * [Deprecated] Registers a rejection handler for a promise.
111 *
112 * This method continues to exist only for BC reasons and to ease upgrading
113 * between versions. It is an alias for:
114 *
115 * ```php
116 * $promise->catch($onRejected);
117 * ```
118 *
119 * @param callable $onRejected
120 * @return PromiseInterface
121 * @deprecated 3.0.0 Use catch() instead
122 * @see self::catch()
123 */
124 public function otherwise(callable $onRejected): PromiseInterface;
125
126 /**
127 * [Deprecated] Allows you to execute "cleanup" type tasks in a promise chain.
128 *
129 * This method continues to exist only for BC reasons and to ease upgrading
130 * between versions. It is an alias for:
131 *
132 * ```php
133 * $promise->finally($onFulfilledOrRejected);
134 * ```
135 *
136 * @param callable $onFulfilledOrRejected
137 * @return PromiseInterface
138 * @deprecated 3.0.0 Use finally() instead
139 * @see self::finally()
140 */
141 public function always(callable $onFulfilledOrRejected): PromiseInterface;
40142 }
+0
-13
src/PromisorInterface.php less more
0 <?php
1
2 namespace React\Promise;
3
4 interface PromisorInterface
5 {
6 /**
7 * Returns the promise of the deferred.
8 *
9 * @return PromiseInterface
10 */
11 public function promise();
12 }
+0
-79
src/RejectedPromise.php less more
0 <?php
1
2 namespace React\Promise;
3
4 /**
5 * @deprecated 2.8.0 External usage of RejectedPromise is deprecated, use `reject()` instead.
6 */
7 class RejectedPromise implements ExtendedPromiseInterface, CancellablePromiseInterface
8 {
9 private $reason;
10
11 public function __construct($reason = null)
12 {
13 if ($reason instanceof PromiseInterface) {
14 throw new \InvalidArgumentException('You cannot create React\Promise\RejectedPromise with a promise. Use React\Promise\reject($promiseOrValue) instead.');
15 }
16
17 $this->reason = $reason;
18 }
19
20 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
21 {
22 if (null === $onRejected) {
23 return $this;
24 }
25
26 try {
27 return resolve($onRejected($this->reason));
28 } catch (\Throwable $exception) {
29 return new RejectedPromise($exception);
30 } catch (\Exception $exception) {
31 return new RejectedPromise($exception);
32 }
33 }
34
35 public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
36 {
37 if (null === $onRejected) {
38 throw UnhandledRejectionException::resolve($this->reason);
39 }
40
41 $result = $onRejected($this->reason);
42
43 if ($result instanceof self) {
44 throw UnhandledRejectionException::resolve($result->reason);
45 }
46
47 if ($result instanceof ExtendedPromiseInterface) {
48 $result->done();
49 }
50 }
51
52 public function otherwise(callable $onRejected)
53 {
54 if (!_checkTypehint($onRejected, $this->reason)) {
55 return $this;
56 }
57
58 return $this->then(null, $onRejected);
59 }
60
61 public function always(callable $onFulfilledOrRejected)
62 {
63 return $this->then(null, function ($reason) use ($onFulfilledOrRejected) {
64 return resolve($onFulfilledOrRejected())->then(function () use ($reason) {
65 return new RejectedPromise($reason);
66 });
67 });
68 }
69
70 public function progress(callable $onProgress)
71 {
72 return $this;
73 }
74
75 public function cancel()
76 {
77 }
78 }
+0
-31
src/UnhandledRejectionException.php less more
0 <?php
1
2 namespace React\Promise;
3
4 class UnhandledRejectionException extends \RuntimeException
5 {
6 private $reason;
7
8 public static function resolve($reason)
9 {
10 if ($reason instanceof \Exception || $reason instanceof \Throwable) {
11 return $reason;
12 }
13
14 return new static($reason);
15 }
16
17 public function __construct($reason)
18 {
19 $this->reason = $reason;
20
21 $message = \sprintf('Unhandled Rejection: %s', \json_encode($reason));
22
23 parent::__construct($message, 0);
24 }
25
26 public function getReason()
27 {
28 return $this->reason;
29 }
30 }
00 <?php
11
22 namespace React\Promise;
3
4 use React\Promise\Exception\CompositeException;
5 use React\Promise\Internal\FulfilledPromise;
6 use React\Promise\Internal\RejectedPromise;
37
48 /**
59 * Creates a promise for the supplied `$promiseOrValue`.
1519 * @param mixed $promiseOrValue
1620 * @return PromiseInterface
1721 */
18 function resolve($promiseOrValue = null)
19 {
20 if ($promiseOrValue instanceof ExtendedPromiseInterface) {
22 function resolve($promiseOrValue): PromiseInterface
23 {
24 if ($promiseOrValue instanceof PromiseInterface) {
2125 return $promiseOrValue;
2226 }
2327
24 // Check is_object() first to avoid method_exists() triggering
25 // class autoloaders if $promiseOrValue is a string.
2628 if (\is_object($promiseOrValue) && \method_exists($promiseOrValue, 'then')) {
2729 $canceller = null;
2830
3032 $canceller = [$promiseOrValue, 'cancel'];
3133 }
3234
33 return new Promise(function ($resolve, $reject, $notify) use ($promiseOrValue) {
34 $promiseOrValue->then($resolve, $reject, $notify);
35 return new Promise(function ($resolve, $reject) use ($promiseOrValue): void {
36 $promiseOrValue->then($resolve, $reject);
3537 }, $canceller);
3638 }
3739
3941 }
4042
4143 /**
42 * Creates a rejected promise for the supplied `$promiseOrValue`.
43 *
44 * If `$promiseOrValue` is a value, it will be the rejection value of the
44 * Creates a rejected promise for the supplied `$reason`.
45 *
46 * If `$reason` is a value, it will be the rejection value of the
4547 * returned promise.
4648 *
47 * If `$promiseOrValue` is a promise, its completion value will be the rejected
49 * If `$reason` is a promise, its completion value will be the rejected
4850 * value of the returned promise.
4951 *
5052 * This can be useful in situations where you need to reject a promise without
5153 * throwing an exception. For example, it allows you to propagate a rejection with
5254 * the value of another promise.
5355 *
54 * @param mixed $promiseOrValue
55 * @return PromiseInterface
56 */
57 function reject($promiseOrValue = null)
58 {
59 if ($promiseOrValue instanceof PromiseInterface) {
60 return resolve($promiseOrValue)->then(function ($value) {
61 return new RejectedPromise($value);
62 });
63 }
64
65 return new RejectedPromise($promiseOrValue);
56 * @param \Throwable $reason
57 * @return PromiseInterface
58 */
59 function reject(\Throwable $reason): PromiseInterface
60 {
61 return new RejectedPromise($reason);
6662 }
6763
6864 /**
7167 * will be an array containing the resolution values of each of the items in
7268 * `$promisesOrValues`.
7369 *
74 * @param array $promisesOrValues
75 * @return PromiseInterface
76 */
77 function all($promisesOrValues)
78 {
79 return map($promisesOrValues, function ($val) {
80 return $val;
81 });
70 * @param iterable $promisesOrValues
71 * @return PromiseInterface
72 */
73 function all(iterable $promisesOrValues): PromiseInterface
74 {
75 $cancellationQueue = new Internal\CancellationQueue();
76
77 return new Promise(function ($resolve, $reject) use ($promisesOrValues, $cancellationQueue): void {
78 $toResolve = 0;
79 $continue = true;
80 $values = [];
81
82 foreach ($promisesOrValues as $i => $promiseOrValue) {
83 $cancellationQueue->enqueue($promiseOrValue);
84 $values[$i] = null;
85 ++$toResolve;
86
87 resolve($promiseOrValue)->then(
88 function ($value) use ($i, &$values, &$toResolve, &$continue, $resolve): void {
89 $values[$i] = $value;
90
91 if (0 === --$toResolve && !$continue) {
92 $resolve($values);
93 }
94 },
95 function (\Throwable $reason) use (&$continue, $reject): void {
96 $continue = false;
97 $reject($reason);
98 }
99 );
100
101 if (!$continue) {
102 break;
103 }
104 }
105
106 $continue = false;
107 if ($toResolve === 0) {
108 $resolve($values);
109 }
110 }, $cancellationQueue);
82111 }
83112
84113 /**
88117 * The returned promise will become **infinitely pending** if `$promisesOrValues`
89118 * contains 0 items.
90119 *
91 * @param array $promisesOrValues
92 * @return PromiseInterface
93 */
94 function race($promisesOrValues)
95 {
96 $cancellationQueue = new CancellationQueue();
97 $cancellationQueue->enqueue($promisesOrValues);
98
99 return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $cancellationQueue) {
100 resolve($promisesOrValues)
101 ->done(function ($array) use ($cancellationQueue, $resolve, $reject, $notify) {
102 if (!is_array($array) || !$array) {
103 $resolve();
104 return;
105 }
106
107 foreach ($array as $promiseOrValue) {
108 $cancellationQueue->enqueue($promiseOrValue);
109
110 resolve($promiseOrValue)
111 ->done($resolve, $reject, $notify);
112 }
113 }, $reject, $notify);
120 * @param iterable $promisesOrValues
121 * @return PromiseInterface
122 */
123 function race(iterable $promisesOrValues): PromiseInterface
124 {
125 $cancellationQueue = new Internal\CancellationQueue();
126
127 return new Promise(function ($resolve, $reject) use ($promisesOrValues, $cancellationQueue): void {
128 $continue = true;
129
130 foreach ($promisesOrValues as $promiseOrValue) {
131 $cancellationQueue->enqueue($promiseOrValue);
132
133 resolve($promiseOrValue)->then($resolve, $reject)->finally(function () use (&$continue): void {
134 $continue = false;
135 });
136
137 if (!$continue) {
138 break;
139 }
140 }
114141 }, $cancellationQueue);
115142 }
116143
125152 * The returned promise will also reject with a `React\Promise\Exception\LengthException`
126153 * if `$promisesOrValues` contains 0 items.
127154 *
128 * @param array $promisesOrValues
129 * @return PromiseInterface
130 */
131 function any($promisesOrValues)
132 {
133 return some($promisesOrValues, 1)
134 ->then(function ($val) {
135 return \array_shift($val);
136 });
137 }
138
139 /**
140 * Returns a promise that will resolve when `$howMany` of the supplied items in
141 * `$promisesOrValues` resolve. The resolution value of the returned promise
142 * will be an array of length `$howMany` containing the resolution values of the
143 * triggering items.
144 *
145 * The returned promise will reject if it becomes impossible for `$howMany` items
146 * to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items
147 * reject). The rejection value will be an array of
148 * `(count($promisesOrValues) - $howMany) + 1` rejection reasons.
149 *
150 * The returned promise will also reject with a `React\Promise\Exception\LengthException`
151 * if `$promisesOrValues` contains less items than `$howMany`.
152 *
153 * @param array $promisesOrValues
154 * @param int $howMany
155 * @return PromiseInterface
156 */
157 function some($promisesOrValues, $howMany)
158 {
159 $cancellationQueue = new CancellationQueue();
160 $cancellationQueue->enqueue($promisesOrValues);
161
162 return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $howMany, $cancellationQueue) {
163 resolve($promisesOrValues)
164 ->done(function ($array) use ($howMany, $cancellationQueue, $resolve, $reject, $notify) {
165 if (!\is_array($array) || $howMany < 1) {
166 $resolve([]);
167 return;
155 * @param iterable $promisesOrValues
156 * @return PromiseInterface
157 */
158 function any(iterable $promisesOrValues): PromiseInterface
159 {
160 $cancellationQueue = new Internal\CancellationQueue();
161
162 return new Promise(function ($resolve, $reject) use ($promisesOrValues, $cancellationQueue): void {
163 $toReject = 0;
164 $continue = true;
165 $reasons = [];
166
167 foreach ($promisesOrValues as $i => $promiseOrValue) {
168 $cancellationQueue->enqueue($promiseOrValue);
169 ++$toReject;
170
171 resolve($promiseOrValue)->then(
172 function ($value) use ($resolve, &$continue): void {
173 $continue = false;
174 $resolve($value);
175 },
176 function (\Throwable $reason) use ($i, &$reasons, &$toReject, $reject, &$continue): void {
177 $reasons[$i] = $reason;
178
179 if (0 === --$toReject && !$continue) {
180 $reject(new CompositeException(
181 $reasons,
182 'All promises rejected.'
183 ));
184 }
168185 }
169
170 $len = \count($array);
171
172 if ($len < $howMany) {
173 throw new Exception\LengthException(
174 \sprintf(
175 'Input array must contain at least %d item%s but contains only %s item%s.',
176 $howMany,
177 1 === $howMany ? '' : 's',
178 $len,
179 1 === $len ? '' : 's'
180 )
181 );
182 }
183
184 $toResolve = $howMany;
185 $toReject = ($len - $toResolve) + 1;
186 $values = [];
187 $reasons = [];
188
189 foreach ($array as $i => $promiseOrValue) {
190 $fulfiller = function ($val) use ($i, &$values, &$toResolve, $toReject, $resolve) {
191 if ($toResolve < 1 || $toReject < 1) {
192 return;
193 }
194
195 $values[$i] = $val;
196
197 if (0 === --$toResolve) {
198 $resolve($values);
199 }
200 };
201
202 $rejecter = function ($reason) use ($i, &$reasons, &$toReject, $toResolve, $reject) {
203 if ($toResolve < 1 || $toReject < 1) {
204 return;
205 }
206
207 $reasons[$i] = $reason;
208
209 if (0 === --$toReject) {
210 $reject($reasons);
211 }
212 };
213
214 $cancellationQueue->enqueue($promiseOrValue);
215
216 resolve($promiseOrValue)
217 ->done($fulfiller, $rejecter, $notify);
218 }
219 }, $reject, $notify);
186 );
187
188 if (!$continue) {
189 break;
190 }
191 }
192
193 $continue = false;
194 if ($toReject === 0 && !$reasons) {
195 $reject(new Exception\LengthException(
196 'Must contain at least 1 item but contains only 0 items.'
197 ));
198 } elseif ($toReject === 0) {
199 $reject(new CompositeException(
200 $reasons,
201 'All promises rejected.'
202 ));
203 }
220204 }, $cancellationQueue);
221205 }
222206
223207 /**
224 * Traditional map function, similar to `array_map()`, but allows input to contain
225 * promises and/or values, and `$mapFunc` may return either a value or a promise.
226 *
227 * The map function receives each item as argument, where item is a fully resolved
228 * value of a promise or value in `$promisesOrValues`.
229 *
230 * @param array $promisesOrValues
231 * @param callable $mapFunc
232 * @return PromiseInterface
233 */
234 function map($promisesOrValues, callable $mapFunc)
235 {
236 $cancellationQueue = new CancellationQueue();
237 $cancellationQueue->enqueue($promisesOrValues);
238
239 return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $mapFunc, $cancellationQueue) {
240 resolve($promisesOrValues)
241 ->done(function ($array) use ($mapFunc, $cancellationQueue, $resolve, $reject, $notify) {
242 if (!\is_array($array) || !$array) {
243 $resolve([]);
244 return;
245 }
246
247 $toResolve = \count($array);
248 $values = [];
249
250 foreach ($array as $i => $promiseOrValue) {
251 $cancellationQueue->enqueue($promiseOrValue);
252 $values[$i] = null;
253
254 resolve($promiseOrValue)
255 ->then($mapFunc)
256 ->done(
257 function ($mapped) use ($i, &$values, &$toResolve, $resolve) {
258 $values[$i] = $mapped;
259
260 if (0 === --$toResolve) {
261 $resolve($values);
262 }
263 },
264 $reject,
265 $notify
266 );
267 }
268 }, $reject, $notify);
269 }, $cancellationQueue);
270 }
271
272 /**
273 * Traditional reduce function, similar to `array_reduce()`, but input may contain
274 * promises and/or values, and `$reduceFunc` may return either a value or a
275 * promise, *and* `$initialValue` may be a promise or a value for the starting
276 * value.
277 *
278 * @param array $promisesOrValues
279 * @param callable $reduceFunc
280 * @param mixed $initialValue
281 * @return PromiseInterface
282 */
283 function reduce($promisesOrValues, callable $reduceFunc, $initialValue = null)
284 {
285 $cancellationQueue = new CancellationQueue();
286 $cancellationQueue->enqueue($promisesOrValues);
287
288 return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $reduceFunc, $initialValue, $cancellationQueue) {
289 resolve($promisesOrValues)
290 ->done(function ($array) use ($reduceFunc, $initialValue, $cancellationQueue, $resolve, $reject, $notify) {
291 if (!\is_array($array)) {
292 $array = [];
293 }
294
295 $total = \count($array);
296 $i = 0;
297
298 // Wrap the supplied $reduceFunc with one that handles promises and then
299 // delegates to the supplied.
300 $wrappedReduceFunc = function ($current, $val) use ($reduceFunc, $cancellationQueue, $total, &$i) {
301 $cancellationQueue->enqueue($val);
302
303 return $current
304 ->then(function ($c) use ($reduceFunc, $total, &$i, $val) {
305 return resolve($val)
306 ->then(function ($value) use ($reduceFunc, $total, &$i, $c) {
307 return $reduceFunc($c, $value, $i++, $total);
308 });
309 });
310 };
311
312 $cancellationQueue->enqueue($initialValue);
313
314 \array_reduce($array, $wrappedReduceFunc, resolve($initialValue))
315 ->done($resolve, $reject, $notify);
316 }, $reject, $notify);
317 }, $cancellationQueue);
318 }
319
320 /**
321208 * @internal
322209 */
323 function _checkTypehint(callable $callback, $object)
324 {
325 if (!\is_object($object)) {
326 return true;
327 }
328
210 function _checkTypehint(callable $callback, \Throwable $reason): bool
211 {
329212 if (\is_array($callback)) {
330213 $callbackReflection = new \ReflectionMethod($callback[0], $callback[1]);
331214 } elseif (\is_object($callback) && !$callback instanceof \Closure) {
342225
343226 $expectedException = $parameters[0];
344227
345 // PHP before v8 used an easy API:
346 if (\PHP_VERSION_ID < 70100 || \defined('HHVM_VERSION')) {
347 if (!$expectedException->getClass()) {
348 return true;
349 }
350
351 return $expectedException->getClass()->isInstance($object);
352 }
353
354228 // Extract the type of the argument and handle different possibilities
355229 $type = $expectedException->getType();
356
230
357231 $isTypeUnion = true;
358232 $types = [];
359233
378252 }
379253
380254 foreach ($types as $type) {
381 if (!$type instanceof \ReflectionNamedType) {
382 throw new \LogicException('This implementation does not support groups of intersection or union types');
383 }
384
385 // A named-type can be either a class-name or a built-in type like string, int, array, etc.
386 $matches = ($type->isBuiltin() && \gettype($object) === $type->getName())
387 || (new \ReflectionClass($type->getName()))->isInstance($object);
388
255
256 if ($type instanceof \ReflectionIntersectionType) {
257 foreach ($type->getTypes() as $typeToMatch) {
258 if (!($matches = ($typeToMatch->isBuiltin() && \gettype($reason) === $typeToMatch->getName())
259 || (new \ReflectionClass($typeToMatch->getName()))->isInstance($reason))) {
260 break;
261 }
262 }
263 } else {
264 $matches = ($type->isBuiltin() && \gettype($reason) === $type->getName())
265 || (new \ReflectionClass($type->getName()))->isInstance($reason);
266 }
389267
390268 // If we look for a single match (union), we can return early on match
391269 // If we look for a full match (intersection), we can return early on mismatch
+0
-100
tests/CancellationQueueTest.php less more
0 <?php
1
2 namespace React\Promise;
3
4 class CancellationQueueTest extends TestCase
5 {
6 /** @test */
7 public function acceptsSimpleCancellableThenable()
8 {
9 $p = new SimpleTestCancellableThenable();
10
11 $cancellationQueue = new CancellationQueue();
12 $cancellationQueue->enqueue($p);
13
14 $cancellationQueue();
15
16 $this->assertTrue($p->cancelCalled);
17 }
18
19 /** @test */
20 public function ignoresSimpleCancellable()
21 {
22 $p = new SimpleTestCancellable();
23
24 $cancellationQueue = new CancellationQueue();
25 $cancellationQueue->enqueue($p);
26
27 $cancellationQueue();
28
29 $this->assertFalse($p->cancelCalled);
30 }
31
32 /** @test */
33 public function callsCancelOnPromisesEnqueuedBeforeStart()
34 {
35 $d1 = $this->getCancellableDeferred();
36 $d2 = $this->getCancellableDeferred();
37
38 $cancellationQueue = new CancellationQueue();
39 $cancellationQueue->enqueue($d1->promise());
40 $cancellationQueue->enqueue($d2->promise());
41
42 $cancellationQueue();
43 }
44
45 /** @test */
46 public function callsCancelOnPromisesEnqueuedAfterStart()
47 {
48 $d1 = $this->getCancellableDeferred();
49 $d2 = $this->getCancellableDeferred();
50
51 $cancellationQueue = new CancellationQueue();
52
53 $cancellationQueue();
54
55 $cancellationQueue->enqueue($d2->promise());
56 $cancellationQueue->enqueue($d1->promise());
57 }
58
59 /** @test */
60 public function doesNotCallCancelTwiceWhenStartedTwice()
61 {
62 $d = $this->getCancellableDeferred();
63
64 $cancellationQueue = new CancellationQueue();
65 $cancellationQueue->enqueue($d->promise());
66
67 $cancellationQueue();
68 $cancellationQueue();
69 }
70
71 /** @test */
72 public function rethrowsExceptionsThrownFromCancel()
73 {
74 $this->setExpectedException('\Exception', 'test');
75
76 $mock = $this
77 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
78 ->getMock();
79 $mock
80 ->expects($this->once())
81 ->method('cancel')
82 ->will($this->throwException(new \Exception('test')));
83
84 $cancellationQueue = new CancellationQueue();
85 $cancellationQueue->enqueue($mock);
86
87 $cancellationQueue();
88 }
89
90 private function getCancellableDeferred()
91 {
92 $mock = $this->createCallableMock();
93 $mock
94 ->expects($this->once())
95 ->method('__invoke');
96
97 return new Deferred($mock);
98 }
99 }
1515 'promise' => [$d, 'promise'],
1616 'resolve' => [$d, 'resolve'],
1717 'reject' => [$d, 'reject'],
18 'notify' => [$d, 'progress'],
1918 'settle' => [$d, 'resolve'],
2019 ]);
21 }
22
23 /** @test */
24 public function progressIsAnAliasForNotify()
25 {
26 $deferred = new Deferred();
27
28 $sentinel = new \stdClass();
29
30 $mock = $this->createCallableMock();
31 $mock
32 ->expects($this->once())
33 ->method('__invoke')
34 ->with($sentinel);
35
36 $deferred->promise()
37 ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock);
38
39 $deferred->progress($sentinel);
4020 }
4121
4222 /** @test */
4323 public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerRejectsWithException()
4424 {
4525 gc_collect_cycles();
46 gc_collect_cycles(); // clear twice to avoid leftovers in PHP 7.4 with ext-xdebug and code coverage turned on
47
4826 $deferred = new Deferred(function ($resolve, $reject) {
4927 $reject(new \Exception('foo'));
5028 });
7351 public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerHoldsReferenceAndExplicitlyRejectWithException()
7452 {
7553 gc_collect_cycles();
54 gc_collect_cycles(); // clear twice to avoid leftovers in PHP 7.4 with ext-xdebug and code coverage turned on
55
7656 $deferred = new Deferred(function () use (&$deferred) { });
7757 $deferred->reject(new \Exception('foo'));
7858 unset($deferred);
7959
8060 $this->assertSame(0, gc_collect_cycles());
8161 }
82
83 /** @test */
84 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingDeferred()
85 {
86 gc_collect_cycles();
87 $deferred = new Deferred();
88 $deferred->promise();
89 unset($deferred);
90
91 $this->assertSame(0, gc_collect_cycles());
92 }
93
94 /** @test */
95 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingDeferredWithUnusedCanceller()
96 {
97 gc_collect_cycles();
98 $deferred = new Deferred(function () { });
99 $deferred->promise();
100 unset($deferred);
101
102 $this->assertSame(0, gc_collect_cycles());
103 }
104
105 /** @test */
106 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingDeferredWithNoopCanceller()
107 {
108 gc_collect_cycles();
109 $deferred = new Deferred(function () { });
110 $deferred->promise()->cancel();
111 unset($deferred);
112
113 $this->assertSame(0, gc_collect_cycles());
114 }
11562 }
+0
-78
tests/FulfilledPromiseTest.php less more
0 <?php
1
2 namespace React\Promise;
3
4 use React\Promise\PromiseAdapter\CallbackPromiseAdapter;
5
6 class FulfilledPromiseTest extends TestCase
7 {
8 use PromiseTest\PromiseSettledTestTrait,
9 PromiseTest\PromiseFulfilledTestTrait;
10
11 public function getPromiseTestAdapter(callable $canceller = null)
12 {
13 $promise = null;
14
15 return new CallbackPromiseAdapter([
16 'promise' => function () use (&$promise) {
17 if (!$promise) {
18 throw new \LogicException('FulfilledPromise must be resolved before obtaining the promise');
19 }
20
21 return $promise;
22 },
23 'resolve' => function ($value = null) use (&$promise) {
24 if (!$promise) {
25 $promise = new FulfilledPromise($value);
26 }
27 },
28 'reject' => function () {
29 throw new \LogicException('You cannot call reject() for React\Promise\FulfilledPromise');
30 },
31 'notify' => function () {
32 // no-op
33 },
34 'settle' => function ($value = null) use (&$promise) {
35 if (!$promise) {
36 $promise = new FulfilledPromise($value);
37 }
38 },
39 ]);
40 }
41
42 /** @test */
43 public function shouldThrowExceptionIfConstructedWithAPromise()
44 {
45 $this->setExpectedException('\InvalidArgumentException');
46
47 return new FulfilledPromise(new FulfilledPromise());
48 }
49
50 /** @test */
51 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToFulfilledPromiseWithAlwaysFollowers()
52 {
53 gc_collect_cycles();
54 gc_collect_cycles(); // clear twice to avoid leftovers in PHP 7.4 with ext-xdebug and code coverage turned on
55
56 $promise = new FulfilledPromise(1);
57 $promise->always(function () {
58 throw new \RuntimeException();
59 });
60 unset($promise);
61
62 $this->assertSame(0, gc_collect_cycles());
63 }
64
65 /** @test */
66 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToFulfilledPromiseWithThenFollowers()
67 {
68 gc_collect_cycles();
69 $promise = new FulfilledPromise(1);
70 $promise = $promise->then(function () {
71 throw new \RuntimeException();
72 });
73 unset($promise);
74
75 $this->assertSame(0, gc_collect_cycles());
76 }
77 }
00 <?php
11
22 namespace React\Promise;
3
4 use Exception;
35
46 class FunctionAllTest extends TestCase
57 {
810 {
911 $mock = $this->createCallableMock();
1012 $mock
11 ->expects($this->once())
13 ->expects(self::once())
1214 ->method('__invoke')
13 ->with($this->identicalTo([]));
15 ->with(self::identicalTo([]));
1416
1517 all([])
1618 ->then($mock);
2123 {
2224 $mock = $this->createCallableMock();
2325 $mock
24 ->expects($this->once())
26 ->expects(self::once())
2527 ->method('__invoke')
26 ->with($this->identicalTo([1, 2, 3]));
28 ->with(self::identicalTo([1, 2, 3]));
2729
2830 all([1, 2, 3])
2931 ->then($mock);
3436 {
3537 $mock = $this->createCallableMock();
3638 $mock
37 ->expects($this->once())
39 ->expects(self::once())
3840 ->method('__invoke')
39 ->with($this->identicalTo([1, 2, 3]));
41 ->with(self::identicalTo([1, 2, 3]));
4042
4143 all([resolve(1), resolve(2), resolve(3)])
4244 ->then($mock);
4749 {
4850 $mock = $this->createCallableMock();
4951 $mock
50 ->expects($this->once())
52 ->expects(self::once())
5153 ->method('__invoke')
52 ->with($this->identicalTo([null, 1, null, 1, 1]));
54 ->with(self::identicalTo([null, 1, null, 1, 1]));
5355
5456 all([null, 1, null, 1, 1])
5557 ->then($mock);
5658 }
5759
5860 /** @test */
59 public function shouldRejectIfAnyInputPromiseRejects()
61 public function shouldResolveValuesGenerator()
6062 {
6163 $mock = $this->createCallableMock();
6264 $mock
63 ->expects($this->once())
65 ->expects(self::once())
6466 ->method('__invoke')
65 ->with($this->identicalTo(2));
67 ->with(self::identicalTo([1, 2, 3]));
6668
67 all([resolve(1), reject(2), resolve(3)])
69 $gen = (function () {
70 for ($i = 1; $i <= 3; ++$i) {
71 yield $i;
72 }
73 })();
74
75 all($gen)->then($mock);
76 }
77
78 /** @test */
79 public function shouldResolveValuesGeneratorEmpty()
80 {
81 $mock = $this->createCallableMock();
82 $mock
83 ->expects(self::once())
84 ->method('__invoke')
85 ->with(self::identicalTo([]));
86
87 $gen = (function () {
88 if (false) {
89 yield;
90 }
91 })();
92
93 all($gen)->then($mock);
94 }
95
96 /** @test */
97 public function shouldRejectIfAnyInputPromiseRejects()
98 {
99 $exception2 = new Exception();
100 $exception3 = new Exception();
101
102 $mock = $this->createCallableMock();
103 $mock
104 ->expects(self::once())
105 ->method('__invoke')
106 ->with(self::identicalTo($exception2));
107
108 all([resolve(1), reject($exception2), resolve($exception3)])
68109 ->then($this->expectCallableNever(), $mock);
69110 }
70111
71112 /** @test */
72 public function shouldAcceptAPromiseForAnArray()
113 public function shouldRejectInfiteGeneratorOrRejectedPromises()
73114 {
74115 $mock = $this->createCallableMock();
75116 $mock
76 ->expects($this->once())
117 ->expects(self::once())
77118 ->method('__invoke')
78 ->with($this->identicalTo([1, 2, 3]));
119 ->with(new \RuntimeException('Iteration 1'));
79120
80 all(resolve([1, 2, 3]))
81 ->then($mock);
82 }
121 $gen = (function () {
122 for ($i = 1; ; ++$i) {
123 yield reject(new \RuntimeException('Iteration ' . $i));
124 }
125 })();
83126
84 /** @test */
85 public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray()
86 {
87 $mock = $this->createCallableMock();
88 $mock
89 ->expects($this->once())
90 ->method('__invoke')
91 ->with($this->identicalTo([]));
92
93 all(resolve(1))
94 ->then($mock);
127 all($gen)->then(null, $mock);
95128 }
96129
97130 /** @test */
99132 {
100133 $mock = $this->createCallableMock();
101134 $mock
102 ->expects($this->once())
135 ->expects(self::once())
103136 ->method('__invoke')
104 ->with($this->identicalTo([1, 2, 3]));
137 ->with(self::identicalTo([1, 2, 3]));
105138
106139 $deferred = new Deferred();
107140
11
22 namespace React\Promise;
33
4 use Exception;
5 use React\Promise\Exception\CompositeException;
46 use React\Promise\Exception\LengthException;
57
68 class FunctionAnyTest extends TestCase
1012 {
1113 $mock = $this->createCallableMock();
1214 $mock
13 ->expects($this->once())
15 ->expects(self::once())
1416 ->method('__invoke')
1517 ->with(
16 $this->callback(function($exception){
18 self::callback(function ($exception) {
1719 return $exception instanceof LengthException &&
18 'Input array must contain at least 1 item but contains only 0 items.' === $exception->getMessage();
20 'Must contain at least 1 item but contains only 0 items.' === $exception->getMessage();
1921 })
2022 );
2123
2426 }
2527
2628 /** @test */
27 public function shouldResolveToNullWithNonArrayInput()
28 {
29 $mock = $this->createCallableMock();
30 $mock
31 ->expects($this->once())
32 ->method('__invoke')
33 ->with($this->identicalTo(null));
34
35 any(null)
36 ->then($mock);
29 public function shouldRejectWithLengthExceptionWithEmptyInputGenerator()
30 {
31 $mock = $this->createCallableMock();
32 $mock
33 ->expects(self::once())
34 ->method('__invoke')
35 ->with(new LengthException('Must contain at least 1 item but contains only 0 items.'));
36
37 $gen = (function () {
38 if (false) {
39 yield;
40 }
41 })();
42
43 any($gen)->then($this->expectCallableNever(), $mock);
3744 }
3845
3946 /** @test */
4148 {
4249 $mock = $this->createCallableMock();
4350 $mock
44 ->expects($this->once())
45 ->method('__invoke')
46 ->with($this->identicalTo(1));
51 ->expects(self::once())
52 ->method('__invoke')
53 ->with(self::identicalTo(1));
4754
4855 any([1, 2, 3])
4956 ->then($mock);
5461 {
5562 $mock = $this->createCallableMock();
5663 $mock
57 ->expects($this->once())
58 ->method('__invoke')
59 ->with($this->identicalTo(1));
64 ->expects(self::once())
65 ->method('__invoke')
66 ->with(self::identicalTo(1));
6067
6168 any([resolve(1), resolve(2), resolve(3)])
6269 ->then($mock);
6370 }
6471
6572 /** @test */
73 public function shouldResolveWithAnInputValueFromDeferred()
74 {
75 $mock = $this->createCallableMock();
76 $mock
77 ->expects(self::once())
78 ->method('__invoke')
79 ->with(self::identicalTo(1));
80
81 $deferred = new Deferred();
82
83 any([$deferred->promise()])->then($mock);
84
85 $deferred->resolve(1);
86 }
87
88 /** @test */
89 public function shouldResolveValuesGenerator()
90 {
91 $mock = $this->createCallableMock();
92 $mock
93 ->expects(self::once())
94 ->method('__invoke')
95 ->with(self::identicalTo(1));
96
97 $gen = (function () {
98 for ($i = 1; $i <= 3; ++$i) {
99 yield $i;
100 }
101 })();
102
103 any($gen)->then($mock);
104 }
105
106 /** @test */
107 public function shouldResolveValuesInfiniteGenerator()
108 {
109 $mock = $this->createCallableMock();
110 $mock
111 ->expects(self::once())
112 ->method('__invoke')
113 ->with(self::identicalTo(1));
114
115 $gen = (function () {
116 for ($i = 1; ; ++$i) {
117 yield $i;
118 }
119 })();
120
121 any($gen)->then($mock);
122 }
123
124 /** @test */
66125 public function shouldRejectWithAllRejectedInputValuesIfAllInputsAreRejected()
67126 {
68 $mock = $this->createCallableMock();
69 $mock
70 ->expects($this->once())
71 ->method('__invoke')
72 ->with($this->identicalTo([0 => 1, 1 => 2, 2 => 3]));
73
74 any([reject(1), reject(2), reject(3)])
127 $exception1 = new Exception();
128 $exception2 = new Exception();
129 $exception3 = new Exception();
130
131 $compositeException = new CompositeException(
132 [0 => $exception1, 1 => $exception2, 2 => $exception3],
133 'All promises rejected.'
134 );
135
136 $mock = $this->createCallableMock();
137 $mock
138 ->expects(self::once())
139 ->method('__invoke')
140 ->with($compositeException);
141
142 any([reject($exception1), reject($exception2), reject($exception3)])
75143 ->then($this->expectCallableNever(), $mock);
76144 }
77145
78146 /** @test */
147 public function shouldRejectWithAllRejectedInputValuesIfInputIsRejectedFromDeferred()
148 {
149 $exception = new Exception();
150
151 $compositeException = new CompositeException(
152 [2 => $exception],
153 'All promises rejected.'
154 );
155
156 $mock = $this->createCallableMock();
157 $mock
158 ->expects(self::once())
159 ->method('__invoke')
160 ->with($compositeException);
161
162 $deferred = new Deferred();
163
164 any([2 => $deferred->promise()])->then($this->expectCallableNever(), $mock);
165
166 $deferred->reject($exception);
167 }
168
169 /** @test */
79170 public function shouldResolveWhenFirstInputPromiseResolves()
80171 {
81 $mock = $this->createCallableMock();
82 $mock
83 ->expects($this->once())
84 ->method('__invoke')
85 ->with($this->identicalTo(1));
86
87 any([resolve(1), reject(2), reject(3)])
88 ->then($mock);
89 }
90
91 /** @test */
92 public function shouldAcceptAPromiseForAnArray()
93 {
94 $mock = $this->createCallableMock();
95 $mock
96 ->expects($this->once())
97 ->method('__invoke')
98 ->with($this->identicalTo(1));
99
100 any(resolve([1, 2, 3]))
101 ->then($mock);
102 }
103
104 /** @test */
105 public function shouldResolveToNullArrayWhenInputPromiseDoesNotResolveToArray()
106 {
107 $mock = $this->createCallableMock();
108 $mock
109 ->expects($this->once())
110 ->method('__invoke')
111 ->with($this->identicalTo(null));
112
113 any(resolve(1))
172 $exception2 = new Exception();
173 $exception3 = new Exception();
174
175 $mock = $this->createCallableMock();
176 $mock
177 ->expects(self::once())
178 ->method('__invoke')
179 ->with(self::identicalTo(1));
180
181 any([resolve(1), reject($exception2), reject($exception3)])
114182 ->then($mock);
115183 }
116184
119187 {
120188 $mock = $this->createCallableMock();
121189 $mock
122 ->expects($this->once())
123 ->method('__invoke')
124 ->with($this->identicalTo(2));
190 ->expects(self::once())
191 ->method('__invoke')
192 ->with(self::identicalTo(2));
125193
126194 $d1 = new Deferred();
127195 $d2 = new Deferred();
134202 }
135203
136204 /** @test */
137 public function shouldRejectWhenInputPromiseRejects()
138 {
139 $mock = $this->createCallableMock();
140 $mock
141 ->expects($this->once())
142 ->method('__invoke')
143 ->with($this->identicalTo(null));
144
145 any(reject())
146 ->then($this->expectCallableNever(), $mock);
147 }
148
149 /** @test */
150 public function shouldCancelInputPromise()
151 {
152 $mock = $this
153 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
154 ->getMock();
155 $mock
156 ->expects($this->once())
157 ->method('cancel');
158
159 any($mock)->cancel();
160 }
161
162 /** @test */
163205 public function shouldCancelInputArrayPromises()
164206 {
165 $mock1 = $this
166 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
167 ->getMock();
168 $mock1
169 ->expects($this->once())
170 ->method('cancel');
171
172 $mock2 = $this
173 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
174 ->getMock();
175 $mock2
176 ->expects($this->once())
177 ->method('cancel');
178
179 any([$mock1, $mock2])->cancel();
207 $promise1 = new Promise(function () {}, $this->expectCallableOnce());
208 $promise2 = new Promise(function () {}, $this->expectCallableOnce());
209
210 any([$promise1, $promise2])->cancel();
180211 }
181212
182213 /** @test */
183214 public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseFulfills()
184215 {
185 $mock = $this->createCallableMock();
186 $mock
187 ->expects($this->never())
188 ->method('__invoke');
189
190
191 $deferred = New Deferred($mock);
192 $deferred->resolve();
193
194 $mock2 = $this
195 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
196 ->getMock();
197 $mock2
198 ->expects($this->never())
199 ->method('cancel');
200
201 some([$deferred->promise(), $mock2], 1)->cancel();
216 $deferred = new Deferred($this->expectCallableNever());
217 $deferred->resolve(null);
218
219 $promise2 = new Promise(function () {}, $this->expectCallableNever());
220
221 any([$deferred->promise(), $promise2], 1)->cancel();
202222 }
203223 }
00 <?php
11
22 namespace React\Promise;
3
4 use Exception;
5 use InvalidArgumentException;
36
47 class FunctionCheckTypehintTest extends TestCase
58 {
69 /** @test */
710 public function shouldAcceptClosureCallbackWithTypehint()
811 {
9 $this->assertTrue(_checkTypehint(function (\InvalidArgumentException $e) {
10 }, new \InvalidArgumentException()));
11 $this->assertfalse(_checkTypehint(function (\InvalidArgumentException $e) {
12 }, new \Exception()));
12 self::assertTrue(_checkTypehint(function (InvalidArgumentException $e) {}, new InvalidArgumentException()));
13 self::assertFalse(_checkTypehint(function (InvalidArgumentException $e) {}, new Exception()));
1314 }
1415
1516 /** @test */
1617 public function shouldAcceptFunctionStringCallbackWithTypehint()
1718 {
18 $this->assertTrue(_checkTypehint('React\Promise\testCallbackWithTypehint', new \InvalidArgumentException()));
19 $this->assertfalse(_checkTypehint('React\Promise\testCallbackWithTypehint', new \Exception()));
19 self::assertTrue(_checkTypehint(new CallbackWithTypehintClass(), new InvalidArgumentException()));
20 self::assertFalse(_checkTypehint(new CallbackWithTypehintClass(), new Exception()));
2021 }
2122
2223 /** @test */
2324 public function shouldAcceptInvokableObjectCallbackWithTypehint()
2425 {
25 $this->assertTrue(_checkTypehint(new CallbackWithTypehintClass(), new \InvalidArgumentException()));
26 $this->assertfalse(_checkTypehint(new CallbackWithTypehintClass(), new \Exception()));
26 self::assertTrue(_checkTypehint(new CallbackWithTypehintClass(), new InvalidArgumentException()));
27 self::assertFalse(_checkTypehint(new CallbackWithTypehintClass(), new Exception()));
2728 }
2829
2930 /** @test */
3031 public function shouldAcceptObjectMethodCallbackWithTypehint()
3132 {
32 $this->assertTrue(_checkTypehint([new CallbackWithTypehintClass(), 'testCallback'], new \InvalidArgumentException()));
33 $this->assertfalse(_checkTypehint([new CallbackWithTypehintClass(), 'testCallback'], new \Exception()));
33 self::assertTrue(_checkTypehint([new CallbackWithTypehintClass(), 'testCallback'], new InvalidArgumentException()));
34 self::assertFalse(_checkTypehint([new CallbackWithTypehintClass(), 'testCallback'], new Exception()));
3435 }
3536
3637 /** @test */
3738 public function shouldAcceptStaticClassCallbackWithTypehint()
3839 {
39 $this->assertTrue(_checkTypehint([new CallbackWithTypehintClass(), 'testCallbackStatic'], new \InvalidArgumentException()));
40 $this->assertfalse(_checkTypehint([new CallbackWithTypehintClass(), 'testCallbackStatic'], new \Exception()));
40 self::assertTrue(_checkTypehint([CallbackWithTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException()));
41 self::assertFalse(_checkTypehint([CallbackWithTypehintClass::class, 'testCallbackStatic'], new Exception()));
4142 }
4243
4344 /**
5960 */
6061 public function shouldAcceptInvokableObjectCallbackWithUnionTypehint()
6162 {
62 self::assertTrue(_checkTypehint(new CallbackWithUnionTypehintClass(), new \InvalidArgumentException()));
63 self::assertFalse(_checkTypehint(new CallbackWithUnionTypehintClass(), new \Exception()));
63 self::assertTrue(_checkTypehint(new CallbackWithUnionTypehintClass(), new InvalidArgumentException()));
64 self::assertFalse(_checkTypehint(new CallbackWithUnionTypehintClass(), new Exception()));
6465 }
6566
6667 /**
6970 */
7071 public function shouldAcceptObjectMethodCallbackWithUnionTypehint()
7172 {
72 self::assertTrue(_checkTypehint([new CallbackWithUnionTypehintClass(), 'testCallback'], new \InvalidArgumentException()));
73 self::assertFalse(_checkTypehint([new CallbackWithUnionTypehintClass(), 'testCallback'], new \Exception()));
73 self::assertTrue(_checkTypehint([new CallbackWithUnionTypehintClass(), 'testCallback'], new InvalidArgumentException()));
74 self::assertFalse(_checkTypehint([new CallbackWithUnionTypehintClass(), 'testCallback'], new Exception()));
7475 }
7576
7677 /**
7980 */
8081 public function shouldAcceptStaticClassCallbackWithUnionTypehint()
8182 {
82 self::assertTrue(_checkTypehint(['React\Promise\CallbackWithUnionTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException()));
83 self::assertFalse(_checkTypehint(['React\Promise\CallbackWithUnionTypehintClass', 'testCallbackStatic'], new \Exception()));
83 self::assertTrue(_checkTypehint([CallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException()));
84 self::assertFalse(_checkTypehint([CallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new Exception()));
8485 }
8586
8687 /**
9091 public function shouldAcceptInvokableObjectCallbackWithIntersectionTypehint()
9192 {
9293 self::assertFalse(_checkTypehint(new CallbackWithIntersectionTypehintClass(), new \RuntimeException()));
93 self::assertFalse(_checkTypehint(new CallbackWithIntersectionTypehintClass(), new CountableNonException()));
9494 self::assertTrue(_checkTypehint(new CallbackWithIntersectionTypehintClass(), new CountableException()));
9595 }
9696
101101 public function shouldAcceptObjectMethodCallbackWithIntersectionTypehint()
102102 {
103103 self::assertFalse(_checkTypehint([new CallbackWithIntersectionTypehintClass(), 'testCallback'], new \RuntimeException()));
104 self::assertFalse(_checkTypehint([new CallbackWithIntersectionTypehintClass(), 'testCallback'], new CountableNonException()));
105104 self::assertTrue(_checkTypehint([new CallbackWithIntersectionTypehintClass(), 'testCallback'], new CountableException()));
106105 }
107106
111110 */
112111 public function shouldAcceptStaticClassCallbackWithIntersectionTypehint()
113112 {
114 self::assertFalse(_checkTypehint(['React\Promise\CallbackWithIntersectionTypehintClass', 'testCallbackStatic'], new \RuntimeException()));
115 self::assertFalse(_checkTypehint(['React\Promise\CallbackWithIntersectionTypehintClass', 'testCallbackStatic'], new CountableNonException()));
116 self::assertTrue(_checkTypehint(['React\Promise\CallbackWithIntersectionTypehintClass', 'testCallbackStatic'], new CountableException()));
113 self::assertFalse(_checkTypehint([CallbackWithIntersectionTypehintClass::class, 'testCallbackStatic'], new \RuntimeException()));
114 self::assertTrue(_checkTypehint([CallbackWithIntersectionTypehintClass::class, 'testCallbackStatic'], new CountableException()));
115 }
116
117 /**
118 * @test
119 * @requires PHP 8.2
120 */
121 public function shouldAcceptInvokableObjectCallbackWithDNFTypehint()
122 {
123 self::assertFalse(_checkTypehint(new CallbackWithDNFTypehintClass(), new \RuntimeException()));
124 self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new ArrayAccessibleException()));
125 self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new CountableException()));
126 }
127
128 /**
129 * @test
130 * @requires PHP 8.2
131 */
132 public function shouldAcceptObjectMethodCallbackWithDNFTypehint()
133 {
134 self::assertFalse(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new \RuntimeException()));
135 self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new CountableException()));
136 self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new ArrayAccessibleException()));
137 }
138
139 /**
140 * @test
141 * @requires PHP 8.2
142 */
143 public function shouldAcceptStaticClassCallbackWithDNFTypehint()
144 {
145 self::assertFalse(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new \RuntimeException()));
146 self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new CountableException()));
147 self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new ArrayAccessibleException()));
117148 }
118149
119150 /** @test */
120151 public function shouldAcceptClosureCallbackWithoutTypehint()
121152 {
122 $this->assertTrue(_checkTypehint(function (\InvalidArgumentException $e) {
123 }, new \InvalidArgumentException()));
153 self::assertTrue(_checkTypehint(function (InvalidArgumentException $e) {
154 }, new InvalidArgumentException()));
124155 }
125156
126157 /** @test */
127158 public function shouldAcceptFunctionStringCallbackWithoutTypehint()
128159 {
129 $this->assertTrue(_checkTypehint('React\Promise\testCallbackWithoutTypehint', new \InvalidArgumentException()));
160 self::assertTrue(_checkTypehint(new CallbackWithoutTypehintClass(), new InvalidArgumentException()));
130161 }
131162
132163 /** @test */
133164 public function shouldAcceptInvokableObjectCallbackWithoutTypehint()
134165 {
135 $this->assertTrue(_checkTypehint(new CallbackWithoutTypehintClass(), new \InvalidArgumentException()));
166 self::assertTrue(_checkTypehint(new CallbackWithoutTypehintClass(), new InvalidArgumentException()));
136167 }
137168
138169 /** @test */
139170 public function shouldAcceptObjectMethodCallbackWithoutTypehint()
140171 {
141 $this->assertTrue(_checkTypehint([new CallbackWithoutTypehintClass(), 'testCallback'], new \InvalidArgumentException()));
172 self::assertTrue(_checkTypehint([new CallbackWithoutTypehintClass(), 'testCallback'], new InvalidArgumentException()));
142173 }
143174
144175 /** @test */
145176 public function shouldAcceptStaticClassCallbackWithoutTypehint()
146177 {
147 $this->assertTrue(_checkTypehint(['React\Promise\CallbackWithoutTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException()));
178 self::assertTrue(_checkTypehint([CallbackWithoutTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException()));
148179 }
149180 }
150181
151 function testCallbackWithTypehint(\InvalidArgumentException $e)
182 function testCallbackWithTypehint(InvalidArgumentException $e)
152183 {
153184 }
154185
+0
-198
tests/FunctionMapTest.php less more
0 <?php
1
2 namespace React\Promise;
3
4 class FunctionMapTest extends TestCase
5 {
6 protected function mapper()
7 {
8 return function ($val) {
9 return $val * 2;
10 };
11 }
12
13 protected function promiseMapper()
14 {
15 return function ($val) {
16 return resolve($val * 2);
17 };
18 }
19
20 /** @test */
21 public function shouldMapInputValuesArray()
22 {
23 $mock = $this->createCallableMock();
24 $mock
25 ->expects($this->once())
26 ->method('__invoke')
27 ->with($this->identicalTo([2, 4, 6]));
28
29 map(
30 [1, 2, 3],
31 $this->mapper()
32 )->then($mock);
33 }
34
35 /** @test */
36 public function shouldMapInputPromisesArray()
37 {
38 $mock = $this->createCallableMock();
39 $mock
40 ->expects($this->once())
41 ->method('__invoke')
42 ->with($this->identicalTo([2, 4, 6]));
43
44 map(
45 [resolve(1), resolve(2), resolve(3)],
46 $this->mapper()
47 )->then($mock);
48 }
49
50 /** @test */
51 public function shouldMapMixedInputArray()
52 {
53 $mock = $this->createCallableMock();
54 $mock
55 ->expects($this->once())
56 ->method('__invoke')
57 ->with($this->identicalTo([2, 4, 6]));
58
59 map(
60 [1, resolve(2), 3],
61 $this->mapper()
62 )->then($mock);
63 }
64
65 /** @test */
66 public function shouldMapInputWhenMapperReturnsAPromise()
67 {
68 $mock = $this->createCallableMock();
69 $mock
70 ->expects($this->once())
71 ->method('__invoke')
72 ->with($this->identicalTo([2, 4, 6]));
73
74 map(
75 [1, 2, 3],
76 $this->promiseMapper()
77 )->then($mock);
78 }
79
80 /** @test */
81 public function shouldAcceptAPromiseForAnArray()
82 {
83 $mock = $this->createCallableMock();
84 $mock
85 ->expects($this->once())
86 ->method('__invoke')
87 ->with($this->identicalTo([2, 4, 6]));
88
89 map(
90 resolve([1, resolve(2), 3]),
91 $this->mapper()
92 )->then($mock);
93 }
94
95 /** @test */
96 public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray()
97 {
98 $mock = $this->createCallableMock();
99 $mock
100 ->expects($this->once())
101 ->method('__invoke')
102 ->with($this->identicalTo([]));
103
104 map(
105 resolve(1),
106 $this->mapper()
107 )->then($mock);
108 }
109
110 /** @test */
111 public function shouldPreserveTheOrderOfArrayWhenResolvingAsyncPromises()
112 {
113 $mock = $this->createCallableMock();
114 $mock
115 ->expects($this->once())
116 ->method('__invoke')
117 ->with($this->identicalTo([2, 4, 6]));
118
119 $deferred = new Deferred();
120
121 map(
122 [resolve(1), $deferred->promise(), resolve(3)],
123 $this->mapper()
124 )->then($mock);
125
126 $deferred->resolve(2);
127 }
128
129 /** @test */
130 public function shouldRejectWhenInputContainsRejection()
131 {
132 $mock = $this->createCallableMock();
133 $mock
134 ->expects($this->once())
135 ->method('__invoke')
136 ->with($this->identicalTo(2));
137
138 map(
139 [resolve(1), reject(2), resolve(3)],
140 $this->mapper()
141 )->then($this->expectCallableNever(), $mock);
142 }
143
144 /** @test */
145 public function shouldRejectWhenInputPromiseRejects()
146 {
147 $mock = $this->createCallableMock();
148 $mock
149 ->expects($this->once())
150 ->method('__invoke')
151 ->with($this->identicalTo(null));
152
153 map(
154 reject(),
155 $this->mapper()
156 )->then($this->expectCallableNever(), $mock);
157 }
158
159 /** @test */
160 public function shouldCancelInputPromise()
161 {
162 $mock = $this
163 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
164 ->getMock();
165 $mock
166 ->expects($this->once())
167 ->method('cancel');
168
169 map(
170 $mock,
171 $this->mapper()
172 )->cancel();
173 }
174
175 /** @test */
176 public function shouldCancelInputArrayPromises()
177 {
178 $mock1 = $this
179 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
180 ->getMock();
181 $mock1
182 ->expects($this->once())
183 ->method('cancel');
184
185 $mock2 = $this
186 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
187 ->getMock();
188 $mock2
189 ->expects($this->once())
190 ->method('cancel');
191
192 map(
193 [$mock1, $mock2],
194 $this->mapper()
195 )->cancel();
196 }
197 }
11
22 namespace React\Promise;
33
4 use Exception;
5
46 class FunctionRaceTest extends TestCase
57 {
68 /** @test */
7 public function shouldResolveEmptyInput()
9 public function shouldReturnForeverPendingPromiseForEmptyInput()
810 {
9 $mock = $this->createCallableMock();
10 $mock
11 ->expects($this->once())
12 ->method('__invoke')
13 ->with($this->identicalTo(null));
14
1511 race(
1612 []
17 )->then($mock);
13 )->then($this->expectCallableNever(), $this->expectCallableNever());
1814 }
1915
2016 /** @test */
2218 {
2319 $mock = $this->createCallableMock();
2420 $mock
25 ->expects($this->once())
21 ->expects(self::once())
2622 ->method('__invoke')
27 ->with($this->identicalTo(1));
23 ->with(self::identicalTo(1));
2824
2925 race(
3026 [1, 2, 3]
3632 {
3733 $mock = $this->createCallableMock();
3834 $mock
39 ->expects($this->once())
35 ->expects(self::once())
4036 ->method('__invoke')
41 ->with($this->identicalTo(2));
37 ->with(self::identicalTo(2));
4238
4339 $d1 = new Deferred();
4440 $d2 = new Deferred();
5955 {
6056 $mock = $this->createCallableMock();
6157 $mock
62 ->expects($this->once())
58 ->expects(self::once())
6359 ->method('__invoke')
64 ->with($this->identicalTo(null));
60 ->with(self::identicalTo(null));
6561
6662 race(
6763 [null, 1, null, 2, 3]
6965 }
7066
7167 /** @test */
72 public function shouldRejectIfFirstSettledPromiseRejects()
68 public function shouldResolveValuesGenerator()
7369 {
7470 $mock = $this->createCallableMock();
7571 $mock
76 ->expects($this->once())
72 ->expects(self::once())
7773 ->method('__invoke')
78 ->with($this->identicalTo(2));
74 ->with(self::identicalTo(1));
75
76 $gen = (function () {
77 for ($i = 1; $i <= 3; ++$i) {
78 yield $i;
79 }
80 })();
81
82 race($gen)->then($mock);
83 }
84
85 /** @test */
86 public function shouldResolveValuesInfiniteGenerator()
87 {
88 $mock = $this->createCallableMock();
89 $mock
90 ->expects(self::once())
91 ->method('__invoke')
92 ->with(self::identicalTo(1));
93
94 $gen = (function () {
95 for ($i = 1; ; ++$i) {
96 yield $i;
97 }
98 })();
99
100 race($gen)->then($mock);
101 }
102
103 /** @test */
104 public function shouldRejectIfFirstSettledPromiseRejects()
105 {
106 $exception = new Exception();
107
108 $mock = $this->createCallableMock();
109 $mock
110 ->expects(self::once())
111 ->method('__invoke')
112 ->with(self::identicalTo($exception));
79113
80114 $d1 = new Deferred();
81115 $d2 = new Deferred();
85119 [$d1->promise(), $d2->promise(), $d3->promise()]
86120 )->then($this->expectCallableNever(), $mock);
87121
88 $d2->reject(2);
122 $d2->reject($exception);
89123
90124 $d1->resolve(1);
91125 $d3->resolve(3);
92126 }
93127
94128 /** @test */
95 public function shouldAcceptAPromiseForAnArray()
96 {
97 $mock = $this->createCallableMock();
98 $mock
99 ->expects($this->once())
100 ->method('__invoke')
101 ->with($this->identicalTo(1));
102
103 race(
104 resolve([1, 2, 3])
105 )->then($mock);
106 }
107
108 /** @test */
109 public function shouldResolveToNullWhenInputPromiseDoesNotResolveToArray()
110 {
111 $mock = $this->createCallableMock();
112 $mock
113 ->expects($this->once())
114 ->method('__invoke')
115 ->with($this->identicalTo(null));
116
117 race(
118 resolve(1)
119 )->then($mock);
120 }
121
122 /** @test */
123 public function shouldRejectWhenInputPromiseRejects()
124 {
125 $mock = $this->createCallableMock();
126 $mock
127 ->expects($this->once())
128 ->method('__invoke')
129 ->with($this->identicalTo(null));
130
131 race(
132 reject()
133 )->then($this->expectCallableNever(), $mock);
134 }
135
136 /** @test */
137 public function shouldCancelInputPromise()
138 {
139 $mock = $this
140 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
141 ->getMock();
142 $mock
143 ->expects($this->once())
144 ->method('cancel');
145
146 race($mock)->cancel();
147 }
148
149 /** @test */
150129 public function shouldCancelInputArrayPromises()
151130 {
152 $mock1 = $this
153 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
154 ->getMock();
155 $mock1
156 ->expects($this->once())
157 ->method('cancel');
131 $promise1 = new Promise(function () {}, $this->expectCallableOnce());
132 $promise2 = new Promise(function () {}, $this->expectCallableOnce());
158133
159 $mock2 = $this
160 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
161 ->getMock();
162 $mock2
163 ->expects($this->once())
164 ->method('cancel');
165
166 race([$mock1, $mock2])->cancel();
134 race([$promise1, $promise2])->cancel();
167135 }
168136
169137 /** @test */
170138 public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseFulfills()
171139 {
172 $mock = $this->createCallableMock();
173 $mock
174 ->expects($this->never())
175 ->method('__invoke');
140 $deferred = new Deferred($this->expectCallableNever());
141 $deferred->resolve(null);
176142
177 $deferred = New Deferred($mock);
178 $deferred->resolve();
143 $promise2 = new Promise(function () {}, $this->expectCallableNever());
179144
180 $mock2 = $this
181 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
182 ->getMock();
183 $mock2
184 ->expects($this->never())
185 ->method('cancel');
186
187 race([$deferred->promise(), $mock2])->cancel();
145 race([$deferred->promise(), $promise2])->cancel();
188146 }
189147
190148 /** @test */
191149 public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseRejects()
192150 {
193 $mock = $this->createCallableMock();
194 $mock
195 ->expects($this->never())
196 ->method('__invoke');
151 $deferred = new Deferred($this->expectCallableNever());
152 $deferred->reject(new Exception());
197153
198 $deferred = New Deferred($mock);
199 $deferred->reject();
154 $promise2 = new Promise(function () {}, $this->expectCallableNever());
200155
201 $mock2 = $this
202 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
203 ->getMock();
204 $mock2
205 ->expects($this->never())
206 ->method('cancel');
207
208 race([$deferred->promise(), $mock2])->cancel();
156 race([$deferred->promise(), $promise2])->cancel();
209157 }
210158 }
+0
-347
tests/FunctionReduceTest.php less more
0 <?php
1
2 namespace React\Promise;
3
4 class FunctionReduceTest extends TestCase
5 {
6 protected function plus()
7 {
8 return function ($sum, $val) {
9 return $sum + $val;
10 };
11 }
12
13 protected function append()
14 {
15 return function ($sum, $val) {
16 return $sum . $val;
17 };
18 }
19
20 /** @test */
21 public function shouldReduceValuesWithoutInitialValue()
22 {
23 $mock = $this->createCallableMock();
24 $mock
25 ->expects($this->once())
26 ->method('__invoke')
27 ->with($this->identicalTo(6));
28
29 reduce(
30 [1, 2, 3],
31 $this->plus()
32 )->then($mock);
33 }
34
35 /** @test */
36 public function shouldReduceValuesWithInitialValue()
37 {
38 $mock = $this->createCallableMock();
39 $mock
40 ->expects($this->once())
41 ->method('__invoke')
42 ->with($this->identicalTo(7));
43
44 reduce(
45 [1, 2, 3],
46 $this->plus(),
47 1
48 )->then($mock);
49 }
50
51 /** @test */
52 public function shouldReduceValuesWithInitialPromise()
53 {
54 $mock = $this->createCallableMock();
55 $mock
56 ->expects($this->once())
57 ->method('__invoke')
58 ->with($this->identicalTo(7));
59
60 reduce(
61 [1, 2, 3],
62 $this->plus(),
63 resolve(1)
64 )->then($mock);
65 }
66
67 /** @test */
68 public function shouldReducePromisedValuesWithoutInitialValue()
69 {
70 $mock = $this->createCallableMock();
71 $mock
72 ->expects($this->once())
73 ->method('__invoke')
74 ->with($this->identicalTo(6));
75
76 reduce(
77 [resolve(1), resolve(2), resolve(3)],
78 $this->plus()
79 )->then($mock);
80 }
81
82 /** @test */
83 public function shouldReducePromisedValuesWithInitialValue()
84 {
85 $mock = $this->createCallableMock();
86 $mock
87 ->expects($this->once())
88 ->method('__invoke')
89 ->with($this->identicalTo(7));
90
91 reduce(
92 [resolve(1), resolve(2), resolve(3)],
93 $this->plus(),
94 1
95 )->then($mock);
96 }
97
98 /** @test */
99 public function shouldReducePromisedValuesWithInitialPromise()
100 {
101 $mock = $this->createCallableMock();
102 $mock
103 ->expects($this->once())
104 ->method('__invoke')
105 ->with($this->identicalTo(7));
106
107 reduce(
108 [resolve(1), resolve(2), resolve(3)],
109 $this->plus(),
110 resolve(1)
111 )->then($mock);
112 }
113
114 /** @test */
115 public function shouldReduceEmptyInputWithInitialValue()
116 {
117 $mock = $this->createCallableMock();
118 $mock
119 ->expects($this->once())
120 ->method('__invoke')
121 ->with($this->identicalTo(1));
122
123 reduce(
124 [],
125 $this->plus(),
126 1
127 )->then($mock);
128 }
129
130 /** @test */
131 public function shouldReduceEmptyInputWithInitialPromise()
132 {
133 $mock = $this->createCallableMock();
134 $mock
135 ->expects($this->once())
136 ->method('__invoke')
137 ->with($this->identicalTo(1));
138
139 reduce(
140 [],
141 $this->plus(),
142 resolve(1)
143 )->then($mock);
144 }
145
146 /** @test */
147 public function shouldRejectWhenInputContainsRejection()
148 {
149 $mock = $this->createCallableMock();
150 $mock
151 ->expects($this->once())
152 ->method('__invoke')
153 ->with($this->identicalTo(2));
154
155 reduce(
156 [resolve(1), reject(2), resolve(3)],
157 $this->plus(),
158 resolve(1)
159 )->then($this->expectCallableNever(), $mock);
160 }
161
162 /** @test */
163 public function shouldResolveWithNullWhenInputIsEmptyAndNoInitialValueOrPromiseProvided()
164 {
165 // Note: this is different from when.js's behavior!
166 // In when.reduce(), this rejects with a TypeError exception (following
167 // JavaScript's [].reduce behavior.
168 // We're following PHP's array_reduce behavior and resolve with NULL.
169 $mock = $this->createCallableMock();
170 $mock
171 ->expects($this->once())
172 ->method('__invoke')
173 ->with($this->identicalTo(null));
174
175 reduce(
176 [],
177 $this->plus()
178 )->then($mock);
179 }
180
181 /** @test */
182 public function shouldAllowSparseArrayInputWithoutInitialValue()
183 {
184 $mock = $this->createCallableMock();
185 $mock
186 ->expects($this->once())
187 ->method('__invoke')
188 ->with($this->identicalTo(3));
189
190 reduce(
191 [null, null, 1, null, 1, 1],
192 $this->plus()
193 )->then($mock);
194 }
195
196 /** @test */
197 public function shouldAllowSparseArrayInputWithInitialValue()
198 {
199 $mock = $this->createCallableMock();
200 $mock
201 ->expects($this->once())
202 ->method('__invoke')
203 ->with($this->identicalTo(4));
204
205 reduce(
206 [null, null, 1, null, 1, 1],
207 $this->plus(),
208 1
209 )->then($mock);
210 }
211
212 /** @test */
213 public function shouldReduceInInputOrder()
214 {
215 $mock = $this->createCallableMock();
216 $mock
217 ->expects($this->once())
218 ->method('__invoke')
219 ->with($this->identicalTo('123'));
220
221 reduce(
222 [1, 2, 3],
223 $this->append(),
224 ''
225 )->then($mock);
226 }
227
228 /** @test */
229 public function shouldAcceptAPromiseForAnArray()
230 {
231 $mock = $this->createCallableMock();
232 $mock
233 ->expects($this->once())
234 ->method('__invoke')
235 ->with($this->identicalTo('123'));
236
237 reduce(
238 resolve([1, 2, 3]),
239 $this->append(),
240 ''
241 )->then($mock);
242 }
243
244 /** @test */
245 public function shouldResolveToInitialValueWhenInputPromiseDoesNotResolveToAnArray()
246 {
247 $mock = $this->createCallableMock();
248 $mock
249 ->expects($this->once())
250 ->method('__invoke')
251 ->with($this->identicalTo(1));
252
253 reduce(
254 resolve(1),
255 $this->plus(),
256 1
257 )->then($mock);
258 }
259
260 /** @test */
261 public function shouldProvideCorrectBasisValue()
262 {
263 $insertIntoArray = function ($arr, $val, $i) {
264 $arr[$i] = $val;
265
266 return $arr;
267 };
268
269 $d1 = new Deferred();
270 $d2 = new Deferred();
271 $d3 = new Deferred();
272
273 $mock = $this->createCallableMock();
274 $mock
275 ->expects($this->once())
276 ->method('__invoke')
277 ->with($this->identicalTo([1, 2, 3]));
278
279 reduce(
280 [$d1->promise(), $d2->promise(), $d3->promise()],
281 $insertIntoArray,
282 []
283 )->then($mock);
284
285 $d3->resolve(3);
286 $d1->resolve(1);
287 $d2->resolve(2);
288 }
289
290 /** @test */
291 public function shouldRejectWhenInputPromiseRejects()
292 {
293 $mock = $this->createCallableMock();
294 $mock
295 ->expects($this->once())
296 ->method('__invoke')
297 ->with($this->identicalTo(null));
298
299 reduce(
300 reject(),
301 $this->plus(),
302 1
303 )->then($this->expectCallableNever(), $mock);
304 }
305
306 /** @test */
307 public function shouldCancelInputPromise()
308 {
309 $mock = $this
310 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
311 ->getMock();
312 $mock
313 ->expects($this->once())
314 ->method('cancel');
315
316 reduce(
317 $mock,
318 $this->plus(),
319 1
320 )->cancel();
321 }
322
323 /** @test */
324 public function shouldCancelInputArrayPromises()
325 {
326 $mock1 = $this
327 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
328 ->getMock();
329 $mock1
330 ->expects($this->once())
331 ->method('cancel');
332
333 $mock2 = $this
334 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
335 ->getMock();
336 $mock2
337 ->expects($this->once())
338 ->method('cancel');
339
340 reduce(
341 [$mock1, $mock2],
342 $this->plus(),
343 1
344 )->cancel();
345 }
346 }
11
22 namespace React\Promise;
33
4 use Exception;
5
46 class FunctionRejectTest extends TestCase
57 {
68 /** @test */
7 public function shouldRejectAnImmediateValue()
9 public function shouldRejectAnException()
810 {
9 $expected = 123;
11 $exception = new Exception();
1012
1113 $mock = $this->createCallableMock();
1214 $mock
13 ->expects($this->once())
15 ->expects(self::once())
1416 ->method('__invoke')
15 ->with($this->identicalTo($expected));
17 ->with(self::identicalTo($exception));
1618
17 reject($expected)
18 ->then(
19 $this->expectCallableNever(),
20 $mock
21 );
22 }
23
24 /** @test */
25 public function shouldRejectAFulfilledPromise()
26 {
27 $expected = 123;
28
29 $resolved = new FulfilledPromise($expected);
30
31 $mock = $this->createCallableMock();
32 $mock
33 ->expects($this->once())
34 ->method('__invoke')
35 ->with($this->identicalTo($expected));
36
37 reject($resolved)
38 ->then(
39 $this->expectCallableNever(),
40 $mock
41 );
42 }
43
44 /** @test */
45 public function shouldRejectARejectedPromise()
46 {
47 $expected = 123;
48
49 $resolved = new RejectedPromise($expected);
50
51 $mock = $this->createCallableMock();
52 $mock
53 ->expects($this->once())
54 ->method('__invoke')
55 ->with($this->identicalTo($expected));
56
57 reject($resolved)
58 ->then(
59 $this->expectCallableNever(),
60 $mock
61 );
19 reject($exception)
20 ->then($this->expectCallableNever(), $mock);
6221 }
6322 }
00 <?php
11
22 namespace React\Promise;
3
4 use React\Promise\Internal\FulfilledPromise;
5 use React\Promise\Internal\RejectedPromise;
6 use Exception;
37
48 class FunctionResolveTest extends TestCase
59 {
1014
1115 $mock = $this->createCallableMock();
1216 $mock
13 ->expects($this->once())
17 ->expects(self::once())
1418 ->method('__invoke')
15 ->with($this->identicalTo($expected));
19 ->with(self::identicalTo($expected));
1620
1721 resolve($expected)
1822 ->then(
3034
3135 $mock = $this->createCallableMock();
3236 $mock
33 ->expects($this->once())
37 ->expects(self::once())
3438 ->method('__invoke')
35 ->with($this->identicalTo($expected));
39 ->with(self::identicalTo($expected));
3640
3741 resolve($resolved)
3842 ->then(
4852
4953 $mock = $this->createCallableMock();
5054 $mock
51 ->expects($this->once())
55 ->expects(self::once())
5256 ->method('__invoke')
53 ->with($this->identicalTo('foo'));
57 ->with(self::identicalTo('foo'));
5458
5559 resolve($thenable)
5660 ->then(
6771 $promise = resolve($thenable);
6872 $promise->cancel();
6973
70 $this->assertTrue($thenable->cancelCalled);
74 self::assertTrue($thenable->cancelCalled);
7175 }
7276
7377 /** @test */
7478 public function shouldRejectARejectedPromise()
7579 {
76 $expected = 123;
80 $exception = new Exception();
7781
78 $resolved = new RejectedPromise($expected);
82 $resolved = new RejectedPromise($exception);
7983
8084 $mock = $this->createCallableMock();
8185 $mock
82 ->expects($this->once())
86 ->expects(self::once())
8387 ->method('__invoke')
84 ->with($this->identicalTo($expected));
88 ->with(self::identicalTo($exception));
8589
8690 resolve($resolved)
8791 ->then(
113117
114118 $mock = $this->createCallableMock();
115119 $mock
116 ->expects($this->once())
120 ->expects(self::once())
117121 ->method('__invoke')
118 ->with($this->identicalTo(true));
122 ->with(self::identicalTo(true));
119123
120124 $result->then($mock);
121125 }
125129 {
126130 $deferreds = [];
127131
128 // @TODO Increase count once global-queue is merged
129 for ($i = 0; $i < 10; $i++) {
132 for ($i = 0; $i < 150; $i++) {
130133 $deferreds[] = $d = new Deferred();
131134 $p = $d->promise();
132135
133136 $last = $p;
134 for ($j = 0; $j < 10; $j++) {
135 $last = $last->then(function($result) {
137 for ($j = 0; $j < 150; $j++) {
138 $last = $last->then(function ($result) {
136139 return $result;
137140 });
138141 }
151154
152155 $mock = $this->createCallableMock();
153156 $mock
154 ->expects($this->once())
157 ->expects(self::once())
155158 ->method('__invoke')
156 ->with($this->identicalTo(true));
159 ->with(self::identicalTo(true));
157160
158161 $deferreds[0]->promise()->then($mock);
159162 }
160
161 /** @test */
162 public function returnsExtendePromiseForSimplePromise()
163 {
164 $promise = $this
165 ->getMockBuilder('React\Promise\PromiseInterface')
166 ->getMock();
167
168 $this->assertInstanceOf('React\Promise\ExtendedPromiseInterface', resolve($promise));
169 }
170163 }
+0
-258
tests/FunctionSomeTest.php less more
0 <?php
1
2 namespace React\Promise;
3
4 use React\Promise\Exception\LengthException;
5
6 class FunctionSomeTest extends TestCase
7 {
8 /** @test */
9 public function shouldRejectWithLengthExceptionWithEmptyInputArray()
10 {
11 $mock = $this->createCallableMock();
12 $mock
13 ->expects($this->once())
14 ->method('__invoke')
15 ->with(
16 $this->callback(function($exception){
17 return $exception instanceof LengthException &&
18 'Input array must contain at least 1 item but contains only 0 items.' === $exception->getMessage();
19 })
20 );
21
22 some(
23 [],
24 1
25 )->then($this->expectCallableNever(), $mock);
26 }
27
28 /** @test */
29 public function shouldRejectWithLengthExceptionWithInputArrayContainingNotEnoughItems()
30 {
31 $mock = $this->createCallableMock();
32 $mock
33 ->expects($this->once())
34 ->method('__invoke')
35 ->with(
36 $this->callback(function($exception){
37 return $exception instanceof LengthException &&
38 'Input array must contain at least 4 items but contains only 3 items.' === $exception->getMessage();
39 })
40 );
41
42 some(
43 [1, 2, 3],
44 4
45 )->then($this->expectCallableNever(), $mock);
46 }
47
48 /** @test */
49 public function shouldResolveToEmptyArrayWithNonArrayInput()
50 {
51 $mock = $this->createCallableMock();
52 $mock
53 ->expects($this->once())
54 ->method('__invoke')
55 ->with($this->identicalTo([]));
56
57 some(
58 null,
59 1
60 )->then($mock);
61 }
62
63 /** @test */
64 public function shouldResolveValuesArray()
65 {
66 $mock = $this->createCallableMock();
67 $mock
68 ->expects($this->once())
69 ->method('__invoke')
70 ->with($this->identicalTo([1, 2]));
71
72 some(
73 [1, 2, 3],
74 2
75 )->then($mock);
76 }
77
78 /** @test */
79 public function shouldResolvePromisesArray()
80 {
81 $mock = $this->createCallableMock();
82 $mock
83 ->expects($this->once())
84 ->method('__invoke')
85 ->with($this->identicalTo([1, 2]));
86
87 some(
88 [resolve(1), resolve(2), resolve(3)],
89 2
90 )->then($mock);
91 }
92
93 /** @test */
94 public function shouldResolveSparseArrayInput()
95 {
96 $mock = $this->createCallableMock();
97 $mock
98 ->expects($this->once())
99 ->method('__invoke')
100 ->with($this->identicalTo([null, 1]));
101
102 some(
103 [null, 1, null, 2, 3],
104 2
105 )->then($mock);
106 }
107
108 /** @test */
109 public function shouldRejectIfAnyInputPromiseRejectsBeforeDesiredNumberOfInputsAreResolved()
110 {
111 $mock = $this->createCallableMock();
112 $mock
113 ->expects($this->once())
114 ->method('__invoke')
115 ->with($this->identicalTo([1 => 2, 2 => 3]));
116
117 some(
118 [resolve(1), reject(2), reject(3)],
119 2
120 )->then($this->expectCallableNever(), $mock);
121 }
122
123 /** @test */
124 public function shouldAcceptAPromiseForAnArray()
125 {
126 $mock = $this->createCallableMock();
127 $mock
128 ->expects($this->once())
129 ->method('__invoke')
130 ->with($this->identicalTo([1, 2]));
131
132 some(
133 resolve([1, 2, 3]),
134 2
135 )->then($mock);
136 }
137
138 /** @test */
139 public function shouldResolveWithEmptyArrayIfHowManyIsLessThanOne()
140 {
141 $mock = $this->createCallableMock();
142 $mock
143 ->expects($this->once())
144 ->method('__invoke')
145 ->with($this->identicalTo([]));
146
147 some(
148 [1],
149 0
150 )->then($mock);
151 }
152
153 /** @test */
154 public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray()
155 {
156 $mock = $this->createCallableMock();
157 $mock
158 ->expects($this->once())
159 ->method('__invoke')
160 ->with($this->identicalTo([]));
161
162 some(
163 resolve(1),
164 1
165 )->then($mock);
166 }
167
168 /** @test */
169 public function shouldRejectWhenInputPromiseRejects()
170 {
171 $mock = $this->createCallableMock();
172 $mock
173 ->expects($this->once())
174 ->method('__invoke')
175 ->with($this->identicalTo(null));
176
177 some(
178 reject(),
179 1
180 )->then($this->expectCallableNever(), $mock);
181 }
182
183 /** @test */
184 public function shouldCancelInputPromise()
185 {
186 $mock = $this
187 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
188 ->getMock();
189 $mock
190 ->expects($this->once())
191 ->method('cancel');
192
193 some($mock, 1)->cancel();
194 }
195
196 /** @test */
197 public function shouldCancelInputArrayPromises()
198 {
199 $mock1 = $this
200 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
201 ->getMock();
202 $mock1
203 ->expects($this->once())
204 ->method('cancel');
205
206 $mock2 = $this
207 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
208 ->getMock();
209 $mock2
210 ->expects($this->once())
211 ->method('cancel');
212
213 some([$mock1, $mock2], 1)->cancel();
214 }
215
216 /** @test */
217 public function shouldNotCancelOtherPendingInputArrayPromisesIfEnoughPromisesFulfill()
218 {
219 $mock = $this->createCallableMock();
220 $mock
221 ->expects($this->never())
222 ->method('__invoke');
223
224 $deferred = New Deferred($mock);
225 $deferred->resolve();
226
227 $mock2 = $this
228 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
229 ->getMock();
230 $mock2
231 ->expects($this->never())
232 ->method('cancel');
233
234 some([$deferred->promise(), $mock2], 1);
235 }
236
237 /** @test */
238 public function shouldNotCancelOtherPendingInputArrayPromisesIfEnoughPromisesReject()
239 {
240 $mock = $this->createCallableMock();
241 $mock
242 ->expects($this->never())
243 ->method('__invoke');
244
245 $deferred = New Deferred($mock);
246 $deferred->reject();
247
248 $mock2 = $this
249 ->getMockBuilder('React\Promise\CancellablePromiseInterface')
250 ->getMock();
251 $mock2
252 ->expects($this->never())
253 ->method('cancel');
254
255 some([$deferred->promise(), $mock2], 2);
256 }
257 }
0 <?php
1
2 namespace React\Promise\Internal;
3
4 use Exception;
5 use React\Promise\Deferred;
6 use React\Promise\SimpleTestCancellable;
7 use React\Promise\SimpleTestCancellableThenable;
8 use React\Promise\TestCase;
9
10 class CancellationQueueTest extends TestCase
11 {
12 /** @test */
13 public function acceptsSimpleCancellableThenable()
14 {
15 $p = new SimpleTestCancellableThenable();
16
17 $cancellationQueue = new CancellationQueue();
18 $cancellationQueue->enqueue($p);
19
20 $cancellationQueue();
21
22 self::assertTrue($p->cancelCalled);
23 }
24
25 /** @test */
26 public function ignoresSimpleCancellable()
27 {
28 $p = new SimpleTestCancellable();
29
30 $cancellationQueue = new CancellationQueue();
31 $cancellationQueue->enqueue($p);
32
33 $cancellationQueue();
34
35 self::assertFalse($p->cancelCalled);
36 }
37
38 /** @test */
39 public function callsCancelOnPromisesEnqueuedBeforeStart()
40 {
41 $d1 = $this->getCancellableDeferred();
42 $d2 = $this->getCancellableDeferred();
43
44 $cancellationQueue = new CancellationQueue();
45 $cancellationQueue->enqueue($d1->promise());
46 $cancellationQueue->enqueue($d2->promise());
47
48 $cancellationQueue();
49 }
50
51 /** @test */
52 public function callsCancelOnPromisesEnqueuedAfterStart()
53 {
54 $d1 = $this->getCancellableDeferred();
55 $d2 = $this->getCancellableDeferred();
56
57 $cancellationQueue = new CancellationQueue();
58
59 $cancellationQueue();
60
61 $cancellationQueue->enqueue($d2->promise());
62 $cancellationQueue->enqueue($d1->promise());
63 }
64
65 /** @test */
66 public function doesNotCallCancelTwiceWhenStartedTwice()
67 {
68 $d = $this->getCancellableDeferred();
69
70 $cancellationQueue = new CancellationQueue();
71 $cancellationQueue->enqueue($d->promise());
72
73 $cancellationQueue();
74 $cancellationQueue();
75 }
76
77 /**
78 * @test
79 */
80 public function rethrowsExceptionsThrownFromCancel()
81 {
82 $this->expectException(Exception::class);
83 $this->expectExceptionMessage('test');
84 $mock = $this->createCallableMock();
85 $mock
86 ->expects(self::once())
87 ->method('__invoke')
88 ->will(self::throwException(new Exception('test')));
89
90 $promise = new SimpleTestCancellableThenable($mock);
91
92 $cancellationQueue = new CancellationQueue();
93 $cancellationQueue->enqueue($promise);
94
95 $cancellationQueue();
96 }
97
98 private function getCancellableDeferred()
99 {
100 return new Deferred($this->expectCallableOnce());
101 }
102 }
0 <?php
1
2 namespace React\Promise\Internal;
3
4 use InvalidArgumentException;
5 use LogicException;
6 use React\Promise\PromiseAdapter\CallbackPromiseAdapter;
7 use React\Promise\PromiseTest\PromiseFulfilledTestTrait;
8 use React\Promise\PromiseTest\PromiseSettledTestTrait;
9 use React\Promise\TestCase;
10
11 class FulfilledPromiseTest extends TestCase
12 {
13 use PromiseSettledTestTrait,
14 PromiseFulfilledTestTrait;
15
16 public function getPromiseTestAdapter(callable $canceller = null)
17 {
18 $promise = null;
19
20 return new CallbackPromiseAdapter([
21 'promise' => function () use (&$promise) {
22 if (!$promise) {
23 throw new LogicException('FulfilledPromise must be resolved before obtaining the promise');
24 }
25
26 return $promise;
27 },
28 'resolve' => function ($value = null) use (&$promise) {
29 if (!$promise) {
30 $promise = new FulfilledPromise($value);
31 }
32 },
33 'reject' => function () {
34 throw new LogicException('You cannot call reject() for React\Promise\FulfilledPromise');
35 },
36 'settle' => function ($value = null) use (&$promise) {
37 if (!$promise) {
38 $promise = new FulfilledPromise($value);
39 }
40 },
41 ]);
42 }
43
44 /**
45 * @test
46 */
47 public function shouldThrowExceptionIfConstructedWithAPromise()
48 {
49 $this->expectException(InvalidArgumentException::class);
50 return new FulfilledPromise(new FulfilledPromise());
51 }
52 }
0 <?php
1
2 namespace React\Promise\Internal;
3
4 use Exception;
5 use LogicException;
6 use React\Promise\PromiseAdapter\CallbackPromiseAdapter;
7 use React\Promise\PromiseTest\PromiseRejectedTestTrait;
8 use React\Promise\PromiseTest\PromiseSettledTestTrait;
9 use React\Promise\TestCase;
10
11 class RejectedPromiseTest extends TestCase
12 {
13 use PromiseSettledTestTrait,
14 PromiseRejectedTestTrait;
15
16 public function getPromiseTestAdapter(callable $canceller = null)
17 {
18 $promise = null;
19
20 return new CallbackPromiseAdapter([
21 'promise' => function () use (&$promise) {
22 if (!$promise) {
23 throw new LogicException('RejectedPromise must be rejected before obtaining the promise');
24 }
25
26 return $promise;
27 },
28 'resolve' => function () {
29 throw new LogicException('You cannot call resolve() for React\Promise\RejectedPromise');
30 },
31 'reject' => function (\Throwable $reason) use (&$promise) {
32 if (!$promise) {
33 $promise = new RejectedPromise($reason);
34 }
35 },
36 'settle' => function ($reason = '') use (&$promise) {
37 if (!$promise) {
38 if (!$reason instanceof Exception) {
39 $reason = new Exception((string) $reason);
40 }
41
42 $promise = new RejectedPromise($reason);
43 }
44 },
45 ]);
46 }
47 }
+0
-107
tests/LazyPromiseTest.php less more
0 <?php
1
2 namespace React\Promise;
3
4 use React\Promise\PromiseAdapter\CallbackPromiseAdapter;
5
6 class LazyPromiseTest extends TestCase
7 {
8 use PromiseTest\FullTestTrait;
9
10 public function getPromiseTestAdapter(callable $canceller = null)
11 {
12 $d = new Deferred($canceller);
13
14 $factory = function () use ($d) {
15 return $d->promise();
16 };
17
18 return new CallbackPromiseAdapter([
19 'promise' => function () use ($factory) {
20 return new LazyPromise($factory);
21 },
22 'resolve' => [$d, 'resolve'],
23 'reject' => [$d, 'reject'],
24 'notify' => [$d, 'progress'],
25 'settle' => [$d, 'resolve'],
26 ]);
27 }
28
29 /** @test */
30 public function shouldNotCallFactoryIfThenIsNotInvoked()
31 {
32 $factory = $this->createCallableMock();
33 $factory
34 ->expects($this->never())
35 ->method('__invoke');
36
37 new LazyPromise($factory);
38 }
39
40 /** @test */
41 public function shouldCallFactoryIfThenIsInvoked()
42 {
43 $factory = $this->createCallableMock();
44 $factory
45 ->expects($this->once())
46 ->method('__invoke');
47
48 $p = new LazyPromise($factory);
49 $p->then();
50 }
51
52 /** @test */
53 public function shouldReturnPromiseFromFactory()
54 {
55 $factory = $this->createCallableMock();
56 $factory
57 ->expects($this->once())
58 ->method('__invoke')
59 ->will($this->returnValue(new FulfilledPromise(1)));
60
61 $onFulfilled = $this->createCallableMock();
62 $onFulfilled
63 ->expects($this->once())
64 ->method('__invoke')
65 ->with($this->identicalTo(1));
66
67 $p = new LazyPromise($factory);
68
69 $p->then($onFulfilled);
70 }
71
72 /** @test */
73 public function shouldReturnPromiseIfFactoryReturnsNull()
74 {
75 $factory = $this->createCallableMock();
76 $factory
77 ->expects($this->once())
78 ->method('__invoke')
79 ->will($this->returnValue(null));
80
81 $p = new LazyPromise($factory);
82 $this->assertInstanceOf('React\\Promise\\PromiseInterface', $p->then());
83 }
84
85 /** @test */
86 public function shouldReturnRejectedPromiseIfFactoryThrowsException()
87 {
88 $exception = new \Exception();
89
90 $factory = $this->createCallableMock();
91 $factory
92 ->expects($this->once())
93 ->method('__invoke')
94 ->will($this->throwException($exception));
95
96 $onRejected = $this->createCallableMock();
97 $onRejected
98 ->expects($this->once())
99 ->method('__invoke')
100 ->with($this->identicalTo($exception));
101
102 $p = new LazyPromise($factory);
103
104 $p->then($this->expectCallableNever(), $onRejected);
105 }
106 }
11
22 namespace React\Promise\PromiseAdapter;
33
4 use React\Promise;
4 use React\Promise\PromiseInterface;
55
66 class CallbackPromiseAdapter implements PromiseAdapterInterface
77 {
1212 $this->callbacks = $callbacks;
1313 }
1414
15 public function promise()
15 public function promise(): ?PromiseInterface
1616 {
17 return call_user_func_array($this->callbacks['promise'], func_get_args());
17 return ($this->callbacks['promise'])(...func_get_args());
1818 }
1919
20 public function resolve()
20 public function resolve(): ?PromiseInterface
2121 {
22 return call_user_func_array($this->callbacks['resolve'], func_get_args());
22 return ($this->callbacks['resolve'])(...func_get_args());
2323 }
2424
25 public function reject()
25 public function reject(): ?PromiseInterface
2626 {
27 return call_user_func_array($this->callbacks['reject'], func_get_args());
27 return ($this->callbacks['reject'])(...func_get_args());
2828 }
2929
30 public function notify()
30 public function settle(): ?PromiseInterface
3131 {
32 return call_user_func_array($this->callbacks['notify'], func_get_args());
33 }
34
35 public function settle()
36 {
37 return call_user_func_array($this->callbacks['settle'], func_get_args());
32 return ($this->callbacks['settle'])(...func_get_args());
3833 }
3934 }
11
22 namespace React\Promise\PromiseAdapter;
33
4 use React\Promise;
4 use React\Promise\PromiseInterface;
55
66 interface PromiseAdapterInterface
77 {
8 public function promise();
9 public function resolve();
10 public function reject();
11 public function notify();
12 public function settle();
8 public function promise(): ?PromiseInterface;
9 public function resolve(): ?PromiseInterface;
10 public function reject(): ?PromiseInterface;
11 public function settle(): ?PromiseInterface;
1312 }
11
22 namespace React\Promise\PromiseTest;
33
4 use Exception;
45 use React\Promise;
6 use React\Promise\PromiseAdapter\PromiseAdapterInterface;
57
68 trait CancelTestTrait
79 {
810 /**
9 * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface
11 * @return PromiseAdapterInterface
1012 */
1113 abstract public function getPromiseTestAdapter(callable $canceller = null);
1214
1416 public function cancelShouldCallCancellerWithResolverArguments()
1517 {
1618 $args = null;
17 $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject, $notify) use (&$args) {
19 $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject) use (&$args) {
1820 $args = func_get_args();
1921 });
2022
2123 $adapter->promise()->cancel();
2224
23 $this->assertCount(3, $args);
24 $this->assertTrue(is_callable($args[0]));
25 $this->assertTrue(is_callable($args[1]));
26 $this->assertTrue(is_callable($args[2]));
25 self::assertCount(2, $args);
26 self::assertTrue(is_callable($args[0]));
27 self::assertTrue(is_callable($args[1]));
2728 }
2829
2930 /** @test */
3637
3738 $adapter->promise()->cancel();
3839
39 $this->assertSame(0, $args);
40 self::assertSame(0, $args);
4041 }
4142
4243 /** @test */
6162 /** @test */
6263 public function cancelShouldRejectPromiseIfCancellerRejects()
6364 {
64 $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject) {
65 $reject(1);
66 });
67
68 $mock = $this->createCallableMock();
69 $mock
70 ->expects($this->once())
71 ->method('__invoke')
72 ->with($this->identicalTo(1));
65 $exception = new Exception();
66
67 $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject) use ($exception) {
68 $reject($exception);
69 });
70
71 $mock = $this->createCallableMock();
72 $mock
73 ->expects($this->once())
74 ->method('__invoke')
75 ->with($this->identicalTo($exception));
7376
7477 $adapter->promise()
7578 ->then($this->expectCallableNever(), $mock);
8083 /** @test */
8184 public function cancelShouldRejectPromiseWithExceptionIfCancellerThrows()
8285 {
83 $e = new \Exception();
86 $e = new Exception();
8487
8588 $adapter = $this->getPromiseTestAdapter(function () use ($e) {
8689 throw $e;
99102 }
100103
101104 /** @test */
102 public function cancelShouldProgressPromiseIfCancellerNotifies()
103 {
104 $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject, $progress) {
105 $progress(1);
106 });
107
108 $mock = $this->createCallableMock();
109 $mock
110 ->expects($this->once())
111 ->method('__invoke')
112 ->with($this->identicalTo(1));
113
114 $adapter->promise()
115 ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock);
116
117 $adapter->promise()->cancel();
118 }
119
120 /** @test */
121105 public function cancelShouldCallCancellerOnlyOnceIfCancellerResolves()
122106 {
123107 $mock = $this->createCallableMock();
125109 ->expects($this->once())
126110 ->method('__invoke')
127111 ->will($this->returnCallback(function ($resolve) {
128 $resolve();
112 $resolve(null);
129113 }));
130114
131115 $adapter = $this->getPromiseTestAdapter($mock);
149133 /** @test */
150134 public function cancelShouldCallCancellerFromDeepNestedPromiseChain()
151135 {
152 $mock = $this->createCallableMock();
153 $mock
154 ->expects($this->once())
155 ->method('__invoke');
156
157 $adapter = $this->getPromiseTestAdapter($mock);
136 $adapter = $this->getPromiseTestAdapter($this->expectCallableOnce());
158137
159138 $promise = $adapter->promise()
160139 ->then(function () {
242221
243222 $adapter->promise()->cancel();
244223 }
224
225 /** @test */
226 public function cancelShouldTriggerCancellerWhenFollowerCancels()
227 {
228 $adapter1 = $this->getPromiseTestAdapter($this->expectCallableOnce());
229
230 $root = $adapter1->promise();
231
232 $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce());
233
234 $follower = $adapter2->promise();
235 $adapter2->resolve($root);
236
237 $follower->cancel();
238 }
239
240 /** @test */
241 public function cancelShouldNotTriggerCancellerWhenCancellingOnlyOneFollower()
242 {
243 $adapter1 = $this->getPromiseTestAdapter($this->expectCallableNever());
244
245 $root = $adapter1->promise();
246
247 $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce());
248
249 $follower1 = $adapter2->promise();
250 $adapter2->resolve($root);
251
252 $adapter3 = $this->getPromiseTestAdapter($this->expectCallableNever());
253 $adapter3->resolve($root);
254
255 $follower1->cancel();
256 }
257
258 /** @test */
259 public function cancelCalledOnFollowerShouldOnlyCancelWhenAllChildrenAndFollowerCancelled()
260 {
261 $adapter1 = $this->getPromiseTestAdapter($this->expectCallableOnce());
262
263 $root = $adapter1->promise();
264
265 $child = $root->then();
266
267 $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce());
268
269 $follower = $adapter2->promise();
270 $adapter2->resolve($root);
271
272 $follower->cancel();
273 $child->cancel();
274 }
275
276 /** @test */
277 public function cancelShouldNotTriggerCancellerWhenCancellingFollowerButNotChildren()
278 {
279 $adapter1 = $this->getPromiseTestAdapter($this->expectCallableNever());
280
281 $root = $adapter1->promise();
282
283 $root->then();
284
285 $adapter2 = $this->getPromiseTestAdapter($this->expectCallableOnce());
286
287 $follower = $adapter2->promise();
288 $adapter2->resolve($root);
289
290 $follower->cancel();
291 }
245292 }
99 PromiseRejectedTestTrait,
1010 ResolveTestTrait,
1111 RejectTestTrait,
12 NotifyTestTrait,
1312 CancelTestTrait;
1413 }
+0
-328
tests/PromiseTest/NotifyTestTrait.php less more
0 <?php
1
2 namespace React\Promise\PromiseTest;
3
4 trait NotifyTestTrait
5 {
6 /**
7 * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface
8 */
9 abstract public function getPromiseTestAdapter(callable $canceller = null);
10
11 /** @test */
12 public function notifyShouldProgress()
13 {
14 $adapter = $this->getPromiseTestAdapter();
15
16 $sentinel = new \stdClass();
17
18 $mock = $this->createCallableMock();
19 $mock
20 ->expects($this->once())
21 ->method('__invoke')
22 ->with($sentinel);
23
24 $adapter->promise()
25 ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock);
26
27 $adapter->notify($sentinel);
28 }
29
30 /** @test */
31 public function notifyShouldPropagateProgressToDownstreamPromises()
32 {
33 $adapter = $this->getPromiseTestAdapter();
34
35 $sentinel = new \stdClass();
36
37 $mock = $this->createCallableMock();
38 $mock
39 ->expects($this->once())
40 ->method('__invoke')
41 ->will($this->returnArgument(0));
42
43 $mock2 = $this->createCallableMock();
44 $mock2
45 ->expects($this->once())
46 ->method('__invoke')
47 ->with($sentinel);
48
49 $adapter->promise()
50 ->then(
51 $this->expectCallableNever(),
52 $this->expectCallableNever(),
53 $mock
54 )
55 ->then(
56 $this->expectCallableNever(),
57 $this->expectCallableNever(),
58 $mock2
59 );
60
61 $adapter->notify($sentinel);
62 }
63
64 /** @test */
65 public function notifyShouldPropagateTransformedProgressToDownstreamPromises()
66 {
67 $adapter = $this->getPromiseTestAdapter();
68
69 $sentinel = new \stdClass();
70
71 $mock = $this->createCallableMock();
72 $mock
73 ->expects($this->once())
74 ->method('__invoke')
75 ->will($this->returnValue($sentinel));
76
77 $mock2 = $this->createCallableMock();
78 $mock2
79 ->expects($this->once())
80 ->method('__invoke')
81 ->with($sentinel);
82
83 $adapter->promise()
84 ->then(
85 $this->expectCallableNever(),
86 $this->expectCallableNever(),
87 $mock
88 )
89 ->then(
90 $this->expectCallableNever(),
91 $this->expectCallableNever(),
92 $mock2
93 );
94
95 $adapter->notify(1);
96 }
97
98 /** @test */
99 public function notifyShouldPropagateCaughtExceptionValueAsProgress()
100 {
101 $adapter = $this->getPromiseTestAdapter();
102
103 $exception = new \Exception();
104
105 $mock = $this->createCallableMock();
106 $mock
107 ->expects($this->once())
108 ->method('__invoke')
109 ->will($this->throwException($exception));
110
111 $mock2 = $this->createCallableMock();
112 $mock2
113 ->expects($this->once())
114 ->method('__invoke')
115 ->with($this->identicalTo($exception));
116
117 $adapter->promise()
118 ->then(
119 $this->expectCallableNever(),
120 $this->expectCallableNever(),
121 $mock
122 )
123 ->then(
124 $this->expectCallableNever(),
125 $this->expectCallableNever(),
126 $mock2
127 );
128
129 $adapter->notify(1);
130 }
131
132 /** @test */
133 public function notifyShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolvedPromiseReturnsAPromise()
134 {
135 $adapter = $this->getPromiseTestAdapter();
136 $adapter2 = $this->getPromiseTestAdapter();
137
138 $promise2 = $adapter2->promise();
139
140 $sentinel = new \stdClass();
141
142 $mock = $this->createCallableMock();
143 $mock
144 ->expects($this->once())
145 ->method('__invoke')
146 ->with($sentinel);
147
148 // resolve BEFORE attaching progress handler
149 $adapter->resolve();
150
151 $adapter->promise()
152 ->then(function () use ($promise2) {
153 return $promise2;
154 })
155 ->then(
156 $this->expectCallableNever(),
157 $this->expectCallableNever(),
158 $mock
159 );
160
161 $adapter2->notify($sentinel);
162 }
163
164 /** @test */
165 public function notifyShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnresolvedPromiseReturnsAPromise()
166 {
167 $adapter = $this->getPromiseTestAdapter();
168 $adapter2 = $this->getPromiseTestAdapter();
169
170 $promise2 = $adapter2->promise();
171
172 $sentinel = new \stdClass();
173
174 $mock = $this->createCallableMock();
175 $mock
176 ->expects($this->once())
177 ->method('__invoke')
178 ->with($sentinel);
179
180 $adapter->promise()
181 ->then(function () use ($promise2) {
182 return $promise2;
183 })
184 ->then(
185 $this->expectCallableNever(),
186 $this->expectCallableNever(),
187 $mock
188 );
189
190 // resolve AFTER attaching progress handler
191 $adapter->resolve();
192 $adapter2->notify($sentinel);
193 }
194
195 /** @test */
196 public function notifyShouldForwardProgressWhenResolvedWithAnotherPromise()
197 {
198 $adapter = $this->getPromiseTestAdapter();
199 $adapter2 = $this->getPromiseTestAdapter();
200
201 $sentinel = new \stdClass();
202
203 $mock = $this->createCallableMock();
204 $mock
205 ->expects($this->once())
206 ->method('__invoke')
207 ->will($this->returnValue($sentinel));
208
209 $mock2 = $this->createCallableMock();
210 $mock2
211 ->expects($this->once())
212 ->method('__invoke')
213 ->with($sentinel);
214
215 $adapter->promise()
216 ->then(
217 $this->expectCallableNever(),
218 $this->expectCallableNever(),
219 $mock
220 )
221 ->then(
222 $this->expectCallableNever(),
223 $this->expectCallableNever(),
224 $mock2
225 );
226
227 $adapter->resolve($adapter2->promise());
228 $adapter2->notify($sentinel);
229 }
230
231 /** @test */
232 public function notifyShouldAllowResolveAfterProgress()
233 {
234 $adapter = $this->getPromiseTestAdapter();
235
236 $mock = $this->createCallableMock();
237 $mock->expects($this->exactly(2))->method('__invoke')->withConsecutive(
238 array($this->identicalTo(1)),
239 array($this->identicalTo(2))
240 );
241
242 $adapter->promise()
243 ->then(
244 $mock,
245 $this->expectCallableNever(),
246 $mock
247 );
248
249 $adapter->notify(1);
250 $adapter->resolve(2);
251 }
252
253 /** @test */
254 public function notifyShouldAllowRejectAfterProgress()
255 {
256 $adapter = $this->getPromiseTestAdapter();
257
258 $mock = $this->createCallableMock();
259 $mock->expects($this->exactly(2))->method('__invoke')->withConsecutive(
260 array($this->identicalTo(1)),
261 array($this->identicalTo(2))
262 );
263
264 $adapter->promise()
265 ->then(
266 $this->expectCallableNever(),
267 $mock,
268 $mock
269 );
270
271 $adapter->notify(1);
272 $adapter->reject(2);
273 }
274
275 /** @test */
276 public function notifyShouldReturnSilentlyOnProgressWhenAlreadyRejected()
277 {
278 $adapter = $this->getPromiseTestAdapter();
279
280 $adapter->reject(1);
281
282 $this->assertNull($adapter->notify());
283 }
284
285 /** @test */
286 public function notifyShouldInvokeProgressHandler()
287 {
288 $adapter = $this->getPromiseTestAdapter();
289
290 $mock = $this->createCallableMock();
291 $mock
292 ->expects($this->once())
293 ->method('__invoke')
294 ->with($this->identicalTo(1));
295
296 $adapter->promise()->progress($mock);
297 $adapter->notify(1);
298 }
299
300 /** @test */
301 public function notifyShouldInvokeProgressHandlerFromDone()
302 {
303 $adapter = $this->getPromiseTestAdapter();
304
305 $mock = $this->createCallableMock();
306 $mock
307 ->expects($this->once())
308 ->method('__invoke')
309 ->with($this->identicalTo(1));
310
311 $this->assertNull($adapter->promise()->done(null, null, $mock));
312 $adapter->notify(1);
313 }
314
315 /** @test */
316 public function notifyShouldThrowExceptionThrownProgressHandlerFromDone()
317 {
318 $adapter = $this->getPromiseTestAdapter();
319
320 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
321
322 $this->assertNull($adapter->promise()->done(null, null, function () {
323 throw new \Exception('UnhandledRejectionException');
324 }));
325 $adapter->notify(1);
326 }
327 }
00 <?php
11
22 namespace React\Promise\PromiseTest;
3
4 use Exception;
5 use React\Promise\PromiseAdapter\PromiseAdapterInterface;
6 use stdClass;
7 use function React\Promise\reject;
8 use function React\Promise\resolve;
39
410 trait PromiseFulfilledTestTrait
511 {
612 /**
7 * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface
13 * @return PromiseAdapterInterface
814 */
915 abstract public function getPromiseTestAdapter(callable $canceller = null);
1016
109115 $adapter->promise()
110116 ->then(
111117 function ($val) {
112 return \React\Promise\resolve($val + 1);
118 return resolve($val + 1);
113119 },
114120 $this->expectCallableNever()
115121 )
124130 {
125131 $adapter = $this->getPromiseTestAdapter();
126132
127 $mock = $this->createCallableMock();
128 $mock
129 ->expects($this->once())
130 ->method('__invoke')
131 ->with($this->identicalTo(2));
132
133 $adapter->resolve(1);
134 $adapter->promise()
135 ->then(
136 function ($val) {
137 return \React\Promise\reject($val + 1);
133 $exception = new Exception();
134
135 $mock = $this->createCallableMock();
136 $mock
137 ->expects($this->once())
138 ->method('__invoke')
139 ->with($this->identicalTo($exception));
140
141 $adapter->resolve(1);
142 $adapter->promise()
143 ->then(
144 function () use ($exception) {
145 return reject($exception);
138146 },
139147 $this->expectCallableNever()
140148 )
149157 {
150158 $adapter = $this->getPromiseTestAdapter();
151159
152 $exception = new \Exception();
160 $exception = new Exception();
153161
154162 $mock = $this->createCallableMock();
155163 $mock
175183 );
176184 }
177185
186 /**
187 * @test
188 * @requires PHP 8.1
189 */
190 public function thenShouldContinueToExecuteCallbacksWhenPriorCallbackSuspendsFiber()
191 {
192 $adapter = $this->getPromiseTestAdapter();
193 $adapter->resolve(42);
194
195 $fiber = new \Fiber(function () use ($adapter) {
196 $adapter->promise()->then(function (int $value) {
197 \Fiber::suspend($value);
198 });
199 });
200
201 $ret = $fiber->start();
202 $this->assertEquals(42, $ret);
203
204 $mock = $this->createCallableMock();
205 $mock
206 ->expects($this->once())
207 ->method('__invoke')
208 ->with($this->identicalTo(42));
209
210 $adapter->promise()->then($mock);
211 }
212
178213 /** @test */
179214 public function cancelShouldReturnNullForFulfilledPromise()
180215 {
181216 $adapter = $this->getPromiseTestAdapter();
182217
183 $adapter->resolve();
184
185 $this->assertNull($adapter->promise()->cancel());
218 $adapter->resolve(null);
219
220 self::assertNull($adapter->promise()->cancel());
186221 }
187222
188223 /** @test */
190225 {
191226 $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
192227
193 $adapter->resolve();
228 $adapter->resolve(null);
194229
195230 $adapter->promise()->cancel();
196231 }
197232
198233 /** @test */
199 public function doneShouldInvokeFulfillmentHandlerForFulfilledPromise()
200 {
201 $adapter = $this->getPromiseTestAdapter();
202
203 $mock = $this->createCallableMock();
204 $mock
205 ->expects($this->once())
206 ->method('__invoke')
207 ->with($this->identicalTo(1));
208
209 $adapter->resolve(1);
210 $this->assertNull($adapter->promise()->done($mock));
211 }
212
213 /** @test */
214 public function doneShouldThrowExceptionThrownFulfillmentHandlerForFulfilledPromise()
215 {
216 $adapter = $this->getPromiseTestAdapter();
217
218 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
219
220 $adapter->resolve(1);
221 $this->assertNull($adapter->promise()->done(function () {
222 throw new \Exception('UnhandledRejectionException');
223 }));
224 }
225
226 /** @test */
227 public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejectsForFulfilledPromise()
228 {
229 $adapter = $this->getPromiseTestAdapter();
230
231 $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
232
233 $adapter->resolve(1);
234 $this->assertNull($adapter->promise()->done(function () {
235 return \React\Promise\reject();
236 }));
237 }
238
239 /** @test */
234 public function catchShouldNotInvokeRejectionHandlerForFulfilledPromise()
235 {
236 $adapter = $this->getPromiseTestAdapter();
237
238 $adapter->resolve(1);
239 $adapter->promise()->catch($this->expectCallableNever());
240 }
241
242 /** @test */
243 public function finallyShouldNotSuppressValueForFulfilledPromise()
244 {
245 $adapter = $this->getPromiseTestAdapter();
246
247 $value = new stdClass();
248
249 $mock = $this->createCallableMock();
250 $mock
251 ->expects($this->once())
252 ->method('__invoke')
253 ->with($this->identicalTo($value));
254
255 $adapter->resolve($value);
256 $adapter->promise()
257 ->finally(function () {})
258 ->then($mock);
259 }
260
261 /** @test */
262 public function finallyShouldNotSuppressValueWhenHandlerReturnsANonPromiseForFulfilledPromise()
263 {
264 $adapter = $this->getPromiseTestAdapter();
265
266 $value = new stdClass();
267
268 $mock = $this->createCallableMock();
269 $mock
270 ->expects($this->once())
271 ->method('__invoke')
272 ->with($this->identicalTo($value));
273
274 $adapter->resolve($value);
275 $adapter->promise()
276 ->finally(function () {
277 return 1;
278 })
279 ->then($mock);
280 }
281
282 /** @test */
283 public function finallyShouldNotSuppressValueWhenHandlerReturnsAPromiseForFulfilledPromise()
284 {
285 $adapter = $this->getPromiseTestAdapter();
286
287 $value = new stdClass();
288
289 $mock = $this->createCallableMock();
290 $mock
291 ->expects($this->once())
292 ->method('__invoke')
293 ->with($this->identicalTo($value));
294
295 $adapter->resolve($value);
296 $adapter->promise()
297 ->finally(function () {
298 return resolve(1);
299 })
300 ->then($mock);
301 }
302
303 /** @test */
304 public function finallyShouldRejectWhenHandlerThrowsForFulfilledPromise()
305 {
306 $adapter = $this->getPromiseTestAdapter();
307
308 $exception = new Exception();
309
310 $mock = $this->createCallableMock();
311 $mock
312 ->expects($this->once())
313 ->method('__invoke')
314 ->with($this->identicalTo($exception));
315
316 $adapter->resolve(1);
317 $adapter->promise()
318 ->finally(function () use ($exception) {
319 throw $exception;
320 })
321 ->then(null, $mock);
322 }
323
324 /** @test */
325 public function finallyShouldRejectWhenHandlerRejectsForFulfilledPromise()
326 {
327 $adapter = $this->getPromiseTestAdapter();
328
329 $exception = new Exception();
330
331 $mock = $this->createCallableMock();
332 $mock
333 ->expects($this->once())
334 ->method('__invoke')
335 ->with($this->identicalTo($exception));
336
337 $adapter->resolve(1);
338 $adapter->promise()
339 ->finally(function () use ($exception) {
340 return reject($exception);
341 })
342 ->then(null, $mock);
343 }
344
345 /**
346 * @test
347 * @deprecated
348 */
240349 public function otherwiseShouldNotInvokeRejectionHandlerForFulfilledPromise()
241350 {
242351 $adapter = $this->getPromiseTestAdapter();
245354 $adapter->promise()->otherwise($this->expectCallableNever());
246355 }
247356
248 /** @test */
357 /**
358 * @test
359 * @deprecated
360 */
249361 public function alwaysShouldNotSuppressValueForFulfilledPromise()
250362 {
251363 $adapter = $this->getPromiseTestAdapter();
252364
253 $value = new \stdClass();
365 $value = new stdClass();
254366
255367 $mock = $this->createCallableMock();
256368 $mock
264376 ->then($mock);
265377 }
266378
267 /** @test */
379 /**
380 * @test
381 * @deprecated
382 */
268383 public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromiseForFulfilledPromise()
269384 {
270385 $adapter = $this->getPromiseTestAdapter();
271386
272 $value = new \stdClass();
387 $value = new stdClass();
273388
274389 $mock = $this->createCallableMock();
275390 $mock
285400 ->then($mock);
286401 }
287402
288 /** @test */
403 /**
404 * @test
405 * @deprecated
406 */
289407 public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromiseForFulfilledPromise()
290408 {
291409 $adapter = $this->getPromiseTestAdapter();
292410
293 $value = new \stdClass();
411 $value = new stdClass();
294412
295413 $mock = $this->createCallableMock();
296414 $mock
301419 $adapter->resolve($value);
302420 $adapter->promise()
303421 ->always(function () {
304 return \React\Promise\resolve(1);
305 })
306 ->then($mock);
307 }
308
309 /** @test */
422 return resolve(1);
423 })
424 ->then($mock);
425 }
426
427 /**
428 * @test
429 * @deprecated
430 */
310431 public function alwaysShouldRejectWhenHandlerThrowsForFulfilledPromise()
311432 {
312433 $adapter = $this->getPromiseTestAdapter();
313434
314 $exception = new \Exception();
435 $exception = new Exception();
315436
316437 $mock = $this->createCallableMock();
317438 $mock
327448 ->then(null, $mock);
328449 }
329450
330 /** @test */
451 /**
452 * @test
453 * @deprecated
454 */
331455 public function alwaysShouldRejectWhenHandlerRejectsForFulfilledPromise()
332456 {
333457 $adapter = $this->getPromiseTestAdapter();
334458
335 $exception = new \Exception();
459 $exception = new Exception();
336460
337461 $mock = $this->createCallableMock();
338462 $mock
343467 $adapter->resolve(1);
344468 $adapter->promise()
345469 ->always(function () use ($exception) {
346 return \React\Promise\reject($exception);
470 return reject($exception);
347471 })
348472 ->then(null, $mock);
349473 }
11
22 namespace React\Promise\PromiseTest;
33
4 use React\Promise\PromiseAdapter\PromiseAdapterInterface;
5 use React\Promise\PromiseInterface;
6
47 trait PromisePendingTestTrait
58 {
69 /**
7 * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface
10 * @return PromiseAdapterInterface
811 */
912 abstract public function getPromiseTestAdapter(callable $canceller = null);
1013
1316 {
1417 $adapter = $this->getPromiseTestAdapter();
1518
16 $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then());
19 self::assertInstanceOf(PromiseInterface::class, $adapter->promise()->then());
1720 }
1821
1922 /** @test */
2124 {
2225 $adapter = $this->getPromiseTestAdapter();
2326
24 $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then(null, null, null));
27 self::assertInstanceOf(PromiseInterface::class, $adapter->promise()->then(null, null));
2528 }
2629
2730 /** @test */
2932 {
3033 $adapter = $this->getPromiseTestAdapter();
3134
32 $this->assertNull($adapter->promise()->cancel());
35 self::assertNull($adapter->promise()->cancel());
3336 }
3437
3538 /** @test */
36 public function doneShouldReturnNullForPendingPromise()
39 public function catchShouldNotInvokeRejectionHandlerForPendingPromise()
3740 {
3841 $adapter = $this->getPromiseTestAdapter();
3942
40 $this->assertNull($adapter->promise()->done());
43 $adapter->settle(null);
44 $adapter->promise()->catch($this->expectCallableNever());
4145 }
4246
4347 /** @test */
44 public function doneShouldReturnAllowNullForPendingPromise()
48 public function finallyShouldReturnAPromiseForPendingPromise()
4549 {
4650 $adapter = $this->getPromiseTestAdapter();
4751
48 $this->assertNull($adapter->promise()->done(null, null, null));
52 self::assertInstanceOf(PromiseInterface::class, $adapter->promise()->finally(function () {}));
4953 }
5054
51 /** @test */
55 /**
56 * @test
57 * @deprecated
58 */
5259 public function otherwiseShouldNotInvokeRejectionHandlerForPendingPromise()
5360 {
5461 $adapter = $this->getPromiseTestAdapter();
5562
56 $adapter->settle();
63 $adapter->settle(null);
5764 $adapter->promise()->otherwise($this->expectCallableNever());
5865 }
5966
60 /** @test */
67 /**
68 * @test
69 * @deprecated
70 */
6171 public function alwaysShouldReturnAPromiseForPendingPromise()
6272 {
6373 $adapter = $this->getPromiseTestAdapter();
6474
65 $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->always(function () {}));
75 self::assertInstanceOf(PromiseInterface::class, $adapter->promise()->always(function () {}));
6676 }
6777 }
11
22 namespace React\Promise\PromiseTest;
33
4 use React\Promise\Deferred;
5 use React\Promise\UnhandledRejectionException;
4 use Exception;
5 use InvalidArgumentException;
6 use React\Promise\PromiseAdapter\PromiseAdapterInterface;
7 use function React\Promise\reject;
8 use function React\Promise\resolve;
69
710 trait PromiseRejectedTestTrait
811 {
912 /**
10 * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface
13 * @return PromiseAdapterInterface
1114 */
1215 abstract public function getPromiseTestAdapter(callable $canceller = null);
1316
1619 {
1720 $adapter = $this->getPromiseTestAdapter();
1821
19 $mock = $this->createCallableMock();
20 $mock
21 ->expects($this->once())
22 ->method('__invoke')
23 ->with($this->identicalTo(1));
24
25 $adapter->reject(1);
26 $adapter->reject(2);
22 $exception1 = new Exception();
23 $exception2 = new Exception();
24
25 $mock = $this->createCallableMock();
26 $mock
27 ->expects($this->once())
28 ->method('__invoke')
29 ->with($this->identicalTo($exception1));
30
31 $adapter->reject($exception1);
32 $adapter->reject($exception2);
2733
2834 $adapter->promise()
2935 ->then(
3743 {
3844 $adapter = $this->getPromiseTestAdapter();
3945
40 $adapter->reject(1);
41
42 $mock = $this->createCallableMock();
43 $mock
44 ->expects($this->once())
45 ->method('__invoke')
46 ->with($this->identicalTo(1));
46 $exception = new Exception();
47
48 $adapter->reject($exception);
49
50 $mock = $this->createCallableMock();
51 $mock
52 ->expects($this->once())
53 ->method('__invoke')
54 ->with($this->identicalTo($exception));
4755
4856 $adapter->promise()
4957 ->then($this->expectCallableNever(), $mock);
6068 ->method('__invoke')
6169 ->with(null);
6270
63 $adapter->reject(1);
71 $adapter->reject(new Exception());
6472 $adapter->promise()
6573 ->then(
6674 $this->expectCallableNever(),
8896 ->method('__invoke')
8997 ->with($this->identicalTo(2));
9098
91 $adapter->reject(1);
92 $adapter->promise()
93 ->then(
94 $this->expectCallableNever(),
95 function ($val) {
96 return $val + 1;
99 $adapter->reject(new Exception());
100 $adapter->promise()
101 ->then(
102 $this->expectCallableNever(),
103 function () {
104 return 2;
97105 }
98106 )
99107 ->then(
113121 ->method('__invoke')
114122 ->with($this->identicalTo(2));
115123
116 $adapter->reject(1);
117 $adapter->promise()
118 ->then(
119 $this->expectCallableNever(),
120 function ($val) {
121 return \React\Promise\resolve($val + 1);
124 $adapter->reject(new Exception());
125 $adapter->promise()
126 ->then(
127 $this->expectCallableNever(),
128 function () {
129 return resolve(2);
122130 }
123131 )
124132 ->then(
132140 {
133141 $adapter = $this->getPromiseTestAdapter();
134142
135 $exception = new \Exception();
143 $exception = new Exception();
136144
137145 $mock = $this->createCallableMock();
138146 $mock
146154 ->method('__invoke')
147155 ->with($this->identicalTo($exception));
148156
149 $adapter->reject(1);
157 $adapter->reject(new Exception());
150158 $adapter->promise()
151159 ->then(
152160 $this->expectCallableNever(),
163171 {
164172 $adapter = $this->getPromiseTestAdapter();
165173
166 $mock = $this->createCallableMock();
167 $mock
168 ->expects($this->once())
169 ->method('__invoke')
170 ->with($this->identicalTo(2));
171
172 $adapter->reject(1);
173 $adapter->promise()
174 ->then(
175 $this->expectCallableNever(),
176 function ($val) {
177 return \React\Promise\reject($val + 1);
174 $exception = new Exception();
175
176 $mock = $this->createCallableMock();
177 $mock
178 ->expects($this->once())
179 ->method('__invoke')
180 ->with($this->identicalTo($exception));
181
182 $adapter->reject(new Exception());
183 $adapter->promise()
184 ->then(
185 $this->expectCallableNever(),
186 function () use ($exception) {
187 return reject($exception);
178188 }
179189 )
180190 ->then(
184194 }
185195
186196 /** @test */
187 public function doneShouldInvokeRejectionHandlerForRejectedPromise()
188 {
189 $adapter = $this->getPromiseTestAdapter();
190
191 $mock = $this->createCallableMock();
192 $mock
193 ->expects($this->once())
194 ->method('__invoke')
195 ->with($this->identicalTo(1));
196
197 $adapter->reject(1);
198 $this->assertNull($adapter->promise()->done(null, $mock));
199 }
200
201 /** @test */
202 public function doneShouldThrowExceptionThrownByRejectionHandlerForRejectedPromise()
203 {
204 $adapter = $this->getPromiseTestAdapter();
205
206 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
207
208 $adapter->reject(1);
209 $this->assertNull($adapter->promise()->done(null, function () {
210 throw new \Exception('UnhandledRejectionException');
211 }));
212 }
213
214 /** @test */
215 public function doneShouldThrowUnhandledRejectionExceptionWhenRejectedWithNonExceptionForRejectedPromise()
216 {
217 $adapter = $this->getPromiseTestAdapter();
218
219 $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
220
221 $adapter->reject(1);
222 $this->assertNull($adapter->promise()->done());
223 }
224
225 /** @test */
226 public function unhandledRejectionExceptionThrownByDoneHoldsRejectionValue()
227 {
228 $adapter = $this->getPromiseTestAdapter();
229
230 $expected = new \stdClass();
231
232 $adapter->reject($expected);
233
234 try {
235 $adapter->promise()->done();
236 } catch (UnhandledRejectionException $e) {
237 $this->assertSame($expected, $e->getReason());
238 return;
239 }
240
241 $this->fail();
242 }
243
244 /** @test */
245 public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRejectsForRejectedPromise()
246 {
247 $adapter = $this->getPromiseTestAdapter();
248
249 $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
250
251 $adapter->reject(1);
252 $this->assertNull($adapter->promise()->done(null, function () {
253 return \React\Promise\reject();
254 }));
255 }
256
257 /** @test */
258 public function doneShouldThrowRejectionExceptionWhenRejectionHandlerRejectsWithExceptionForRejectedPromise()
259 {
260 $adapter = $this->getPromiseTestAdapter();
261
262 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
263
264 $adapter->reject(1);
265 $this->assertNull($adapter->promise()->done(null, function () {
266 return \React\Promise\reject(new \Exception('UnhandledRejectionException'));
267 }));
268 }
269
270 /** @test */
271 public function doneShouldThrowExceptionProvidedAsRejectionValueForRejectedPromise()
272 {
273 $adapter = $this->getPromiseTestAdapter();
274
275 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
276
277 $adapter->reject(new \Exception('UnhandledRejectionException'));
278 $this->assertNull($adapter->promise()->done());
279 }
280
281 /** @test */
282 public function doneShouldThrowWithDeepNestingPromiseChainsForRejectedPromise()
283 {
284 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
285
286 $exception = new \Exception('UnhandledRejectionException');
287
288 $d = new Deferred();
289 $d->resolve();
290
291 $result = \React\Promise\resolve(\React\Promise\resolve($d->promise()->then(function () use ($exception) {
292 $d = new Deferred();
293 $d->resolve();
294
295 return \React\Promise\resolve($d->promise()->then(function () {}))->then(
296 function () use ($exception) {
297 throw $exception;
298 }
299 );
300 })));
301
302 $result->done();
303 }
304
305 /** @test */
306 public function doneShouldRecoverWhenRejectionHandlerCatchesExceptionForRejectedPromise()
307 {
308 $adapter = $this->getPromiseTestAdapter();
309
310 $adapter->reject(new \Exception('UnhandledRejectionException'));
311 $this->assertNull($adapter->promise()->done(null, function (\Exception $e) {
312
313 }));
314 }
315
316 /** @test */
197 public function catchShouldInvokeRejectionHandlerForRejectedPromise()
198 {
199 $adapter = $this->getPromiseTestAdapter();
200
201 $exception = new Exception();
202
203 $mock = $this->createCallableMock();
204 $mock
205 ->expects($this->once())
206 ->method('__invoke')
207 ->with($this->identicalTo($exception));
208
209 $adapter->reject($exception);
210 $adapter->promise()->catch($mock);
211 }
212
213 /** @test */
214 public function catchShouldInvokeNonTypeHintedRejectionHandlerIfReasonIsAnExceptionForRejectedPromise()
215 {
216 $adapter = $this->getPromiseTestAdapter();
217
218 $exception = new Exception();
219
220 $mock = $this->createCallableMock();
221 $mock
222 ->expects($this->once())
223 ->method('__invoke')
224 ->with($this->identicalTo($exception));
225
226 $adapter->reject($exception);
227 $adapter->promise()
228 ->catch(function ($reason) use ($mock) {
229 $mock($reason);
230 });
231 }
232
233 /** @test */
234 public function catchShouldInvokeRejectionHandlerIfReasonMatchesTypehintForRejectedPromise()
235 {
236 $adapter = $this->getPromiseTestAdapter();
237
238 $exception = new InvalidArgumentException();
239
240 $mock = $this->createCallableMock();
241 $mock
242 ->expects($this->once())
243 ->method('__invoke')
244 ->with($this->identicalTo($exception));
245
246 $adapter->reject($exception);
247 $adapter->promise()
248 ->catch(function (InvalidArgumentException $reason) use ($mock) {
249 $mock($reason);
250 });
251 }
252
253 /** @test */
254 public function catchShouldNotInvokeRejectionHandlerIfReaonsDoesNotMatchTypehintForRejectedPromise()
255 {
256 $adapter = $this->getPromiseTestAdapter();
257
258 $exception = new Exception();
259
260 $mock = $this->expectCallableNever();
261
262 $adapter->reject($exception);
263 $adapter->promise()
264 ->catch(function (InvalidArgumentException $reason) use ($mock) {
265 $mock($reason);
266 });
267 }
268
269 /** @test */
270 public function finallyShouldNotSuppressRejectionForRejectedPromise()
271 {
272 $adapter = $this->getPromiseTestAdapter();
273
274 $exception = new Exception();
275
276 $mock = $this->createCallableMock();
277 $mock
278 ->expects($this->once())
279 ->method('__invoke')
280 ->with($this->identicalTo($exception));
281
282 $adapter->reject($exception);
283 $adapter->promise()
284 ->finally(function () {})
285 ->then(null, $mock);
286 }
287
288 /** @test */
289 public function finallyShouldNotSuppressRejectionWhenHandlerReturnsANonPromiseForRejectedPromise()
290 {
291 $adapter = $this->getPromiseTestAdapter();
292
293 $exception = new Exception();
294
295 $mock = $this->createCallableMock();
296 $mock
297 ->expects($this->once())
298 ->method('__invoke')
299 ->with($this->identicalTo($exception));
300
301 $adapter->reject($exception);
302 $adapter->promise()
303 ->finally(function () {
304 return 1;
305 })
306 ->then(null, $mock);
307 }
308
309 /** @test */
310 public function finallyShouldNotSuppressRejectionWhenHandlerReturnsAPromiseForRejectedPromise()
311 {
312 $adapter = $this->getPromiseTestAdapter();
313
314 $exception = new Exception();
315
316 $mock = $this->createCallableMock();
317 $mock
318 ->expects($this->once())
319 ->method('__invoke')
320 ->with($this->identicalTo($exception));
321
322 $adapter->reject($exception);
323 $adapter->promise()
324 ->finally(function () {
325 return resolve(1);
326 })
327 ->then(null, $mock);
328 }
329
330 /** @test */
331 public function finallyShouldRejectWhenHandlerThrowsForRejectedPromise()
332 {
333 $adapter = $this->getPromiseTestAdapter();
334
335 $exception1 = new Exception();
336 $exception2 = new Exception();
337
338 $mock = $this->createCallableMock();
339 $mock
340 ->expects($this->once())
341 ->method('__invoke')
342 ->with($this->identicalTo($exception2));
343
344 $adapter->reject($exception1);
345 $adapter->promise()
346 ->finally(function () use ($exception2) {
347 throw $exception2;
348 })
349 ->then(null, $mock);
350 }
351
352 /** @test */
353 public function finallyShouldRejectWhenHandlerRejectsForRejectedPromise()
354 {
355 $adapter = $this->getPromiseTestAdapter();
356
357 $exception1 = new Exception();
358 $exception2 = new Exception();
359
360 $mock = $this->createCallableMock();
361 $mock
362 ->expects($this->once())
363 ->method('__invoke')
364 ->with($this->identicalTo($exception2));
365
366 $adapter->reject($exception1);
367 $adapter->promise()
368 ->finally(function () use ($exception2) {
369 return reject($exception2);
370 })
371 ->then(null, $mock);
372 }
373
374 /** @test */
375 public function cancelShouldReturnNullForRejectedPromise()
376 {
377 $adapter = $this->getPromiseTestAdapter();
378
379 $adapter->reject(new Exception());
380
381 self::assertNull($adapter->promise()->cancel());
382 }
383
384 /** @test */
385 public function cancelShouldHaveNoEffectForRejectedPromise()
386 {
387 $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
388
389 $adapter->reject(new Exception());
390
391 $adapter->promise()->cancel();
392 }
393
394 /**
395 * @test
396 * @deprecated
397 */
317398 public function otherwiseShouldInvokeRejectionHandlerForRejectedPromise()
318399 {
319400 $adapter = $this->getPromiseTestAdapter();
320401
321 $mock = $this->createCallableMock();
322 $mock
323 ->expects($this->once())
324 ->method('__invoke')
325 ->with($this->identicalTo(1));
326
327 $adapter->reject(1);
402 $exception = new Exception();
403
404 $mock = $this->createCallableMock();
405 $mock
406 ->expects($this->once())
407 ->method('__invoke')
408 ->with($this->identicalTo($exception));
409
410 $adapter->reject($exception);
328411 $adapter->promise()->otherwise($mock);
329412 }
330413
331 /** @test */
414 /**
415 * @test
416 * @deprecated
417 */
332418 public function otherwiseShouldInvokeNonTypeHintedRejectionHandlerIfReasonIsAnExceptionForRejectedPromise()
333419 {
334420 $adapter = $this->getPromiseTestAdapter();
335421
336 $exception = new \Exception();
422 $exception = new Exception();
337423
338424 $mock = $this->createCallableMock();
339425 $mock
348434 });
349435 }
350436
351 /** @test */
437 /**
438 * @test
439 * @deprecated
440 */
352441 public function otherwiseShouldInvokeRejectionHandlerIfReasonMatchesTypehintForRejectedPromise()
353442 {
354443 $adapter = $this->getPromiseTestAdapter();
355444
356 $exception = new \InvalidArgumentException();
357
358 $mock = $this->createCallableMock();
359 $mock
360 ->expects($this->once())
361 ->method('__invoke')
362 ->with($this->identicalTo($exception));
363
364 $adapter->reject($exception);
365 $adapter->promise()
366 ->otherwise(function (\InvalidArgumentException $reason) use ($mock) {
445 $exception = new InvalidArgumentException();
446
447 $mock = $this->createCallableMock();
448 $mock
449 ->expects($this->once())
450 ->method('__invoke')
451 ->with($this->identicalTo($exception));
452
453 $adapter->reject($exception);
454 $adapter->promise()
455 ->otherwise(function (InvalidArgumentException $reason) use ($mock) {
367456 $mock($reason);
368457 });
369458 }
370459
371 /** @test */
460 /**
461 * @test
462 * @deprecated
463 */
372464 public function otherwiseShouldNotInvokeRejectionHandlerIfReaonsDoesNotMatchTypehintForRejectedPromise()
373465 {
374466 $adapter = $this->getPromiseTestAdapter();
375467
376 $exception = new \Exception();
468 $exception = new Exception();
377469
378470 $mock = $this->expectCallableNever();
379471
380472 $adapter->reject($exception);
381473 $adapter->promise()
382 ->otherwise(function (\InvalidArgumentException $reason) use ($mock) {
474 ->otherwise(function (InvalidArgumentException $reason) use ($mock) {
383475 $mock($reason);
384476 });
385477 }
386478
387 /** @test */
479 /**
480 * @test
481 * @deprecated
482 */
388483 public function alwaysShouldNotSuppressRejectionForRejectedPromise()
389484 {
390485 $adapter = $this->getPromiseTestAdapter();
391486
392 $exception = new \Exception();
487 $exception = new Exception();
393488
394489 $mock = $this->createCallableMock();
395490 $mock
403498 ->then(null, $mock);
404499 }
405500
406 /** @test */
501 /**
502 * @test
503 * @deprecated
504 */
407505 public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromiseForRejectedPromise()
408506 {
409507 $adapter = $this->getPromiseTestAdapter();
410508
411 $exception = new \Exception();
509 $exception = new Exception();
510
511 $mock = $this->createCallableMock();
512 $mock
513 ->expects($this->once())
514 ->method('__invoke')
515 ->with($this->identicalTo($exception));
516
517 $adapter->reject($exception);
518 $adapter->promise()
519 ->finally(function () {
520 return 1;
521 })
522 ->then(null, $mock);
523 }
524
525 /**
526 * @test
527 * @deprecated
528 */
529 public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromiseForRejectedPromise()
530 {
531 $adapter = $this->getPromiseTestAdapter();
532
533 $exception = new Exception();
412534
413535 $mock = $this->createCallableMock();
414536 $mock
419541 $adapter->reject($exception);
420542 $adapter->promise()
421543 ->always(function () {
422 return 1;
423 })
424 ->then(null, $mock);
425 }
426
427 /** @test */
428 public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromiseForRejectedPromise()
429 {
430 $adapter = $this->getPromiseTestAdapter();
431
432 $exception = new \Exception();
433
434 $mock = $this->createCallableMock();
435 $mock
436 ->expects($this->once())
437 ->method('__invoke')
438 ->with($this->identicalTo($exception));
439
440 $adapter->reject($exception);
441 $adapter->promise()
442 ->always(function () {
443 return \React\Promise\resolve(1);
444 })
445 ->then(null, $mock);
446 }
447
448 /** @test */
544 return resolve(1);
545 })
546 ->then(null, $mock);
547 }
548
549 /**
550 * @test
551 * @deprecated
552 */
449553 public function alwaysShouldRejectWhenHandlerThrowsForRejectedPromise()
450554 {
451555 $adapter = $this->getPromiseTestAdapter();
452556
453 $exception1 = new \Exception();
454 $exception2 = new \Exception();
557 $exception1 = new Exception();
558 $exception2 = new Exception();
455559
456560 $mock = $this->createCallableMock();
457561 $mock
467571 ->then(null, $mock);
468572 }
469573
470 /** @test */
574 /**
575 * @test
576 * @deprecated
577 */
471578 public function alwaysShouldRejectWhenHandlerRejectsForRejectedPromise()
472579 {
473580 $adapter = $this->getPromiseTestAdapter();
474581
475 $exception1 = new \Exception();
476 $exception2 = new \Exception();
582 $exception1 = new Exception();
583 $exception2 = new Exception();
477584
478585 $mock = $this->createCallableMock();
479586 $mock
484591 $adapter->reject($exception1);
485592 $adapter->promise()
486593 ->always(function () use ($exception2) {
487 return \React\Promise\reject($exception2);
488 })
489 ->then(null, $mock);
490 }
491
492 /** @test */
493 public function cancelShouldReturnNullForRejectedPromise()
494 {
495 $adapter = $this->getPromiseTestAdapter();
496
497 $adapter->reject();
498
499 $this->assertNull($adapter->promise()->cancel());
500 }
501
502 /** @test */
503 public function cancelShouldHaveNoEffectForRejectedPromise()
504 {
505 $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
506
507 $adapter->reject();
508
509 $adapter->promise()->cancel();
594 return reject($exception2);
595 })
596 ->then(null, $mock);
510597 }
511598 }
11
22 namespace React\Promise\PromiseTest;
33
4 use React\Promise\PromiseAdapter\PromiseAdapterInterface;
5 use React\Promise\PromiseInterface;
6
47 trait PromiseSettledTestTrait
58 {
69 /**
7 * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface
10 * @return PromiseAdapterInterface
811 */
912 abstract public function getPromiseTestAdapter(callable $canceller = null);
1013
1316 {
1417 $adapter = $this->getPromiseTestAdapter();
1518
16 $adapter->settle();
17 $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then());
19 $adapter->settle(null);
20 self::assertInstanceOf(PromiseInterface::class, $adapter->promise()->then());
1821 }
1922
2023 /** @test */
2225 {
2326 $adapter = $this->getPromiseTestAdapter();
2427
25 $adapter->settle();
26 $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then(null, null, null));
28 $adapter->settle(null);
29 self::assertInstanceOf(PromiseInterface::class, $adapter->promise()->then(null, null));
2730 }
2831
2932 /** @test */
3134 {
3235 $adapter = $this->getPromiseTestAdapter();
3336
34 $adapter->settle();
37 $adapter->settle(null);
3538
36 $this->assertNull($adapter->promise()->cancel());
39 self::assertNull($adapter->promise()->cancel());
3740 }
3841
3942 /** @test */
4144 {
4245 $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
4346
44 $adapter->settle();
47 $adapter->settle(null);
4548
4649 $adapter->promise()->cancel();
4750 }
4851
4952 /** @test */
50 public function doneShouldReturnNullForSettledPromise()
53 public function finallyShouldReturnAPromiseForSettledPromise()
5154 {
5255 $adapter = $this->getPromiseTestAdapter();
5356
54 $adapter->settle();
55 $this->assertNull($adapter->promise()->done(null, function () {}));
57 $adapter->settle(null);
58 self::assertInstanceOf(PromiseInterface::class, $adapter->promise()->finally(function () {}));
5659 }
5760
58 /** @test */
59 public function doneShouldReturnAllowNullForSettledPromise()
60 {
61 $adapter = $this->getPromiseTestAdapter();
62
63 $adapter->settle();
64 $this->assertNull($adapter->promise()->done(null, function () {}, null));
65 }
66
67 /** @test */
68 public function progressShouldNotInvokeProgressHandlerForSettledPromise()
69 {
70 $adapter = $this->getPromiseTestAdapter();
71
72 $adapter->settle();
73 $adapter->promise()->progress($this->expectCallableNever());
74 $adapter->notify();
75 }
76
77 /** @test */
61 /**
62 * @test
63 * @deprecated
64 */
7865 public function alwaysShouldReturnAPromiseForSettledPromise()
7966 {
8067 $adapter = $this->getPromiseTestAdapter();
8168
82 $adapter->settle();
83 $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->always(function () {}));
69 $adapter->settle(null);
70 self::assertInstanceOf(PromiseInterface::class, $adapter->promise()->always(function () {}));
8471 }
8572 }
11
22 namespace React\Promise\PromiseTest;
33
4 use Exception;
45 use React\Promise;
56 use React\Promise\Deferred;
7 use React\Promise\PromiseAdapter\PromiseAdapterInterface;
8 use function React\Promise\reject;
9 use function React\Promise\resolve;
610
711 trait RejectTestTrait
812 {
913 /**
10 * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface
14 * @return PromiseAdapterInterface
1115 */
1216 abstract public function getPromiseTestAdapter(callable $canceller = null);
1317
1418 /** @test */
15 public function rejectShouldRejectWithAnImmediateValue()
16 {
17 $adapter = $this->getPromiseTestAdapter();
18
19 $mock = $this->createCallableMock();
20 $mock
21 ->expects($this->once())
22 ->method('__invoke')
23 ->with($this->identicalTo(1));
19 public function rejectShouldRejectWithAnException()
20 {
21 $adapter = $this->getPromiseTestAdapter();
22
23 $exception = new Exception();
24
25 $mock = $this->createCallableMock();
26 $mock
27 ->expects($this->once())
28 ->method('__invoke')
29 ->with($this->identicalTo($exception));
2430
2531 $adapter->promise()
2632 ->then($this->expectCallableNever(), $mock);
2733
28 $adapter->reject(1);
29 }
30
31 /** @test */
32 public function rejectShouldRejectWithFulfilledPromise()
33 {
34 $adapter = $this->getPromiseTestAdapter();
35
36 $mock = $this->createCallableMock();
37 $mock
38 ->expects($this->once())
39 ->method('__invoke')
40 ->with($this->identicalTo(1));
41
42 $adapter->promise()
43 ->then($this->expectCallableNever(), $mock);
44
45 $adapter->reject(Promise\resolve(1));
46 }
47
48 /** @test */
49 public function rejectShouldRejectWithRejectedPromise()
50 {
51 $adapter = $this->getPromiseTestAdapter();
52
53 $mock = $this->createCallableMock();
54 $mock
55 ->expects($this->once())
56 ->method('__invoke')
57 ->with($this->identicalTo(1));
58
59 $adapter->promise()
60 ->then($this->expectCallableNever(), $mock);
61
62 $adapter->reject(Promise\reject(1));
34 $adapter->reject($exception);
6335 }
6436
6537 /** @test */
6739 {
6840 $adapter = $this->getPromiseTestAdapter();
6941
70 $mock = $this->createCallableMock();
71 $mock
72 ->expects($this->once())
73 ->method('__invoke')
74 ->with($this->identicalTo(1));
42 $exception = new Exception();
43
44 $mock = $this->createCallableMock();
45 $mock
46 ->expects($this->once())
47 ->method('__invoke')
48 ->with($this->identicalTo($exception));
7549
7650 $adapter->promise()
7751 ->then(
8256 $mock
8357 );
8458
85 $adapter->reject(1);
59 $adapter->reject($exception);
8660 }
8761
8862 /** @test */
9064 {
9165 $adapter = $this->getPromiseTestAdapter();
9266
93 $mock = $this->createCallableMock();
94 $mock
95 ->expects($this->once())
96 ->method('__invoke')
97 ->with($this->identicalTo(1));
98
99 $adapter->promise()
100 ->then(null, function ($value) use ($adapter) {
101 $adapter->reject(3);
102
103 return Promise\reject($value);
67 $exception1 = new Exception();
68 $exception2 = new Exception();
69 $exception3 = new Exception();
70
71 $mock = $this->createCallableMock();
72 $mock
73 ->expects($this->once())
74 ->method('__invoke')
75 ->with($this->identicalTo($exception1));
76
77 $adapter->promise()
78 ->then(null, function ($value) use ($exception3, $adapter) {
79 $adapter->reject($exception3);
80
81 return reject($value);
10482 })
10583 ->then(
10684 $this->expectCallableNever(),
10785 $mock
10886 );
10987
110 $adapter->reject(1);
111 $adapter->reject(2);
112 }
113
114 /** @test */
115 public function notifyShouldInvokeOtherwiseHandler()
116 {
117 $adapter = $this->getPromiseTestAdapter();
118
119 $mock = $this->createCallableMock();
120 $mock
121 ->expects($this->once())
122 ->method('__invoke')
123 ->with($this->identicalTo(1));
124
125 $adapter->promise()
126 ->otherwise($mock);
127
128 $adapter->reject(1);
129 }
130
131 /** @test */
132 public function doneShouldInvokeRejectionHandler()
133 {
134 $adapter = $this->getPromiseTestAdapter();
135
136 $mock = $this->createCallableMock();
137 $mock
138 ->expects($this->once())
139 ->method('__invoke')
140 ->with($this->identicalTo(1));
141
142 $this->assertNull($adapter->promise()->done(null, $mock));
143 $adapter->reject(1);
144 }
145
146 /** @test */
147 public function doneShouldThrowExceptionThrownByRejectionHandler()
148 {
149 $adapter = $this->getPromiseTestAdapter();
150
151 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
152
153 $this->assertNull($adapter->promise()->done(null, function () {
154 throw new \Exception('UnhandledRejectionException');
155 }));
156 $adapter->reject(1);
157 }
158
159 /** @test */
160 public function doneShouldThrowUnhandledRejectionExceptionWhenRejectedWithNonException()
161 {
162 $adapter = $this->getPromiseTestAdapter();
163
164 $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
165
166 $this->assertNull($adapter->promise()->done());
167 $adapter->reject(1);
168 }
169
170 /** @test */
171 public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRejects()
172 {
173 $adapter = $this->getPromiseTestAdapter();
174
175 $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
176
177 $this->assertNull($adapter->promise()->done(null, function () {
178 return \React\Promise\reject();
179 }));
180 $adapter->reject(1);
181 }
182
183 /** @test */
184 public function doneShouldThrowRejectionExceptionWhenRejectionHandlerRejectsWithException()
185 {
186 $adapter = $this->getPromiseTestAdapter();
187
188 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
189
190 $this->assertNull($adapter->promise()->done(null, function () {
191 return \React\Promise\reject(new \Exception('UnhandledRejectionException'));
192 }));
193 $adapter->reject(1);
194 }
195
196 /** @test */
197 public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRetunsPendingPromiseWhichRejectsLater()
198 {
199 $adapter = $this->getPromiseTestAdapter();
200
201 $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
202
203 $d = new Deferred();
204 $promise = $d->promise();
205
206 $this->assertNull($adapter->promise()->done(null, function () use ($promise) {
207 return $promise;
208 }));
209 $adapter->reject(1);
210 $d->reject(1);
211 }
212
213 /** @test */
214 public function doneShouldThrowExceptionProvidedAsRejectionValue()
215 {
216 $adapter = $this->getPromiseTestAdapter();
217
218 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
219
220 $this->assertNull($adapter->promise()->done());
221 $adapter->reject(new \Exception('UnhandledRejectionException'));
222 }
223
224 /** @test */
225 public function doneShouldThrowWithDeepNestingPromiseChains()
226 {
227 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
228
229 $exception = new \Exception('UnhandledRejectionException');
230
231 $d = new Deferred();
232
233 $result = \React\Promise\resolve(\React\Promise\resolve($d->promise()->then(function () use ($exception) {
234 $d = new Deferred();
235 $d->resolve();
236
237 return \React\Promise\resolve($d->promise()->then(function () {}))->then(
238 function () use ($exception) {
239 throw $exception;
240 }
241 );
242 })));
243
244 $result->done();
245
246 $d->resolve();
247 }
248
249 /** @test */
250 public function doneShouldRecoverWhenRejectionHandlerCatchesException()
251 {
252 $adapter = $this->getPromiseTestAdapter();
253
254 $this->assertNull($adapter->promise()->done(null, function (\Exception $e) {
255
256 }));
257 $adapter->reject(new \Exception('UnhandledRejectionException'));
258 }
259
260 /** @test */
261 public function alwaysShouldNotSuppressRejection()
262 {
263 $adapter = $this->getPromiseTestAdapter();
264
265 $exception = new \Exception();
266
267 $mock = $this->createCallableMock();
268 $mock
269 ->expects($this->once())
270 ->method('__invoke')
271 ->with($this->identicalTo($exception));
272
273 $adapter->promise()
274 ->always(function () {})
275 ->then(null, $mock);
276
277 $adapter->reject($exception);
278 }
279
280 /** @test */
281 public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromise()
282 {
283 $adapter = $this->getPromiseTestAdapter();
284
285 $exception = new \Exception();
286
287 $mock = $this->createCallableMock();
288 $mock
289 ->expects($this->once())
290 ->method('__invoke')
291 ->with($this->identicalTo($exception));
292
293 $adapter->promise()
294 ->always(function () {
88 $adapter->reject($exception1);
89 $adapter->reject($exception2);
90 }
91
92 /** @test */
93 public function rejectShouldInvokeCatchHandler()
94 {
95 $adapter = $this->getPromiseTestAdapter();
96
97 $exception = new Exception();
98
99 $mock = $this->createCallableMock();
100 $mock
101 ->expects($this->once())
102 ->method('__invoke')
103 ->with($this->identicalTo($exception));
104
105 $adapter->promise()
106 ->catch($mock);
107
108 $adapter->reject($exception);
109 }
110
111 /** @test */
112 public function finallyShouldNotSuppressRejection()
113 {
114 $adapter = $this->getPromiseTestAdapter();
115
116 $exception = new Exception();
117
118 $mock = $this->createCallableMock();
119 $mock
120 ->expects($this->once())
121 ->method('__invoke')
122 ->with($this->identicalTo($exception));
123
124 $adapter->promise()
125 ->finally(function () {})
126 ->then(null, $mock);
127
128 $adapter->reject($exception);
129 }
130
131 /** @test */
132 public function finallyShouldNotSuppressRejectionWhenHandlerReturnsANonPromise()
133 {
134 $adapter = $this->getPromiseTestAdapter();
135
136 $exception = new Exception();
137
138 $mock = $this->createCallableMock();
139 $mock
140 ->expects($this->once())
141 ->method('__invoke')
142 ->with($this->identicalTo($exception));
143
144 $adapter->promise()
145 ->finally(function () {
295146 return 1;
296147 })
297148 ->then(null, $mock);
300151 }
301152
302153 /** @test */
303 public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromise()
304 {
305 $adapter = $this->getPromiseTestAdapter();
306
307 $exception = new \Exception();
308
309 $mock = $this->createCallableMock();
310 $mock
311 ->expects($this->once())
312 ->method('__invoke')
313 ->with($this->identicalTo($exception));
314
315 $adapter->promise()
316 ->always(function () {
317 return \React\Promise\resolve(1);
318 })
319 ->then(null, $mock);
320
321 $adapter->reject($exception);
322 }
323
324 /** @test */
325 public function alwaysShouldRejectWhenHandlerThrowsForRejection()
326 {
327 $adapter = $this->getPromiseTestAdapter();
328
329 $exception = new \Exception();
330
331 $mock = $this->createCallableMock();
332 $mock
333 ->expects($this->once())
334 ->method('__invoke')
335 ->with($this->identicalTo($exception));
336
337 $adapter->promise()
338 ->always(function () use ($exception) {
154 public function finallyShouldNotSuppressRejectionWhenHandlerReturnsAPromise()
155 {
156 $adapter = $this->getPromiseTestAdapter();
157
158 $exception = new Exception();
159
160 $mock = $this->createCallableMock();
161 $mock
162 ->expects($this->once())
163 ->method('__invoke')
164 ->with($this->identicalTo($exception));
165
166 $adapter->promise()
167 ->finally(function () {
168 return resolve(1);
169 })
170 ->then(null, $mock);
171
172 $adapter->reject($exception);
173 }
174
175 /** @test */
176 public function finallyShouldRejectWhenHandlerThrowsForRejection()
177 {
178 $adapter = $this->getPromiseTestAdapter();
179
180 $exception = new Exception();
181
182 $mock = $this->createCallableMock();
183 $mock
184 ->expects($this->once())
185 ->method('__invoke')
186 ->with($this->identicalTo($exception));
187
188 $adapter->promise()
189 ->finally(function () use ($exception) {
339190 throw $exception;
340191 })
341192 ->then(null, $mock);
344195 }
345196
346197 /** @test */
347 public function alwaysShouldRejectWhenHandlerRejectsForRejection()
348 {
349 $adapter = $this->getPromiseTestAdapter();
350
351 $exception = new \Exception();
352
353 $mock = $this->createCallableMock();
354 $mock
355 ->expects($this->once())
356 ->method('__invoke')
357 ->with($this->identicalTo($exception));
358
359 $adapter->promise()
360 ->always(function () use ($exception) {
361 return \React\Promise\reject($exception);
198 public function finallyShouldRejectWhenHandlerRejectsForRejection()
199 {
200 $adapter = $this->getPromiseTestAdapter();
201
202 $exception = new Exception();
203
204 $mock = $this->createCallableMock();
205 $mock
206 ->expects($this->once())
207 ->method('__invoke')
208 ->with($this->identicalTo($exception));
209
210 $adapter->promise()
211 ->finally(function () use ($exception) {
212 return reject($exception);
362213 })
363214 ->then(null, $mock);
364215
11
22 namespace React\Promise\PromiseTest;
33
4 use Exception;
5 use LogicException;
46 use React\Promise;
7 use React\Promise\PromiseAdapter\PromiseAdapterInterface;
8 use stdClass;
9 use function React\Promise\reject;
10 use function React\Promise\resolve;
511
612 trait ResolveTestTrait
713 {
814 /**
9 * @return \React\Promise\PromiseAdapter\PromiseAdapterInterface
15 * @return PromiseAdapterInterface
1016 */
1117 abstract public function getPromiseTestAdapter(callable $canceller = null);
1218
4147 $adapter->promise()
4248 ->then($mock);
4349
44 $adapter->resolve(Promise\resolve(1));
50 $adapter->resolve(resolve(1));
4551 }
4652
4753 /** @test */
4955 {
5056 $adapter = $this->getPromiseTestAdapter();
5157
52 $mock = $this->createCallableMock();
53 $mock
54 ->expects($this->once())
55 ->method('__invoke')
56 ->with($this->identicalTo(1));
58 $exception = new Exception();
59
60 $mock = $this->createCallableMock();
61 $mock
62 ->expects($this->once())
63 ->method('__invoke')
64 ->with($this->identicalTo($exception));
5765
5866 $adapter->promise()
5967 ->then($this->expectCallableNever(), $mock);
6068
61 $adapter->resolve(Promise\reject(1));
69 $adapter->resolve(reject($exception));
6270 }
6371
6472 /** @test */
122130 $mock
123131 ->expects($this->once())
124132 ->method('__invoke')
125 ->with(new \LogicException('Cannot resolve a promise with itself.'));
133 ->with(new LogicException('Cannot resolve a promise with itself.'));
126134
127135 $adapter->promise()
128136 ->then(
145153 $mock
146154 ->expects($this->once())
147155 ->method('__invoke')
148 ->with(new \LogicException('Cannot resolve a promise with itself.'));
156 ->with(new LogicException('Cannot resolve a promise with itself.'));
149157
150158 $promise1 = $adapter1->promise();
151159
161169 }
162170
163171 /** @test */
164 public function doneShouldInvokeFulfillmentHandler()
165 {
166 $adapter = $this->getPromiseTestAdapter();
167
168 $mock = $this->createCallableMock();
169 $mock
170 ->expects($this->once())
171 ->method('__invoke')
172 ->with($this->identicalTo(1));
173
174 $this->assertNull($adapter->promise()->done($mock));
175 $adapter->resolve(1);
176 }
177
178 /** @test */
179 public function doneShouldThrowExceptionThrownFulfillmentHandler()
180 {
181 $adapter = $this->getPromiseTestAdapter();
182
183 $this->setExpectedException('\Exception', 'UnhandledRejectionException');
184
185 $this->assertNull($adapter->promise()->done(function () {
186 throw new \Exception('UnhandledRejectionException');
187 }));
188 $adapter->resolve(1);
189 }
190
191 /** @test */
192 public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejects()
193 {
194 $adapter = $this->getPromiseTestAdapter();
195
196 $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
197
198 $this->assertNull($adapter->promise()->done(function () {
199 return \React\Promise\reject();
200 }));
201 $adapter->resolve(1);
202 }
203
204 /** @test */
205 public function alwaysShouldNotSuppressValue()
206 {
207 $adapter = $this->getPromiseTestAdapter();
208
209 $value = new \stdClass();
172 public function finallyShouldNotSuppressValue()
173 {
174 $adapter = $this->getPromiseTestAdapter();
175
176 $value = new stdClass();
210177
211178 $mock = $this->createCallableMock();
212179 $mock
215182 ->with($this->identicalTo($value));
216183
217184 $adapter->promise()
218 ->always(function () {})
185 ->finally(function () {})
219186 ->then($mock);
220187
221188 $adapter->resolve($value);
222189 }
223190
224191 /** @test */
225 public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromise()
226 {
227 $adapter = $this->getPromiseTestAdapter();
228
229 $value = new \stdClass();
192 public function finallyShouldNotSuppressValueWhenHandlerReturnsANonPromise()
193 {
194 $adapter = $this->getPromiseTestAdapter();
195
196 $value = new stdClass();
230197
231198 $mock = $this->createCallableMock();
232199 $mock
235202 ->with($this->identicalTo($value));
236203
237204 $adapter->promise()
238 ->always(function () {
205 ->finally(function () {
239206 return 1;
240207 })
241208 ->then($mock);
244211 }
245212
246213 /** @test */
247 public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromise()
248 {
249 $adapter = $this->getPromiseTestAdapter();
250
251 $value = new \stdClass();
214 public function finallyShouldNotSuppressValueWhenHandlerReturnsAPromise()
215 {
216 $adapter = $this->getPromiseTestAdapter();
217
218 $value = new stdClass();
252219
253220 $mock = $this->createCallableMock();
254221 $mock
257224 ->with($this->identicalTo($value));
258225
259226 $adapter->promise()
260 ->always(function () {
261 return \React\Promise\resolve(1);
227 ->finally(function () {
228 return resolve(1);
262229 })
263230 ->then($mock);
264231
266233 }
267234
268235 /** @test */
269 public function alwaysShouldRejectWhenHandlerThrowsForFulfillment()
270 {
271 $adapter = $this->getPromiseTestAdapter();
272
273 $exception = new \Exception();
236 public function finallyShouldRejectWhenHandlerThrowsForFulfillment()
237 {
238 $adapter = $this->getPromiseTestAdapter();
239
240 $exception = new Exception();
274241
275242 $mock = $this->createCallableMock();
276243 $mock
279246 ->with($this->identicalTo($exception));
280247
281248 $adapter->promise()
282 ->always(function () use ($exception) {
249 ->finally(function () use ($exception) {
283250 throw $exception;
284251 })
285252 ->then(null, $mock);
288255 }
289256
290257 /** @test */
291 public function alwaysShouldRejectWhenHandlerRejectsForFulfillment()
292 {
293 $adapter = $this->getPromiseTestAdapter();
294
295 $exception = new \Exception();
258 public function finallyShouldRejectWhenHandlerRejectsForFulfillment()
259 {
260 $adapter = $this->getPromiseTestAdapter();
261
262 $exception = new Exception();
296263
297264 $mock = $this->createCallableMock();
298265 $mock
301268 ->with($this->identicalTo($exception));
302269
303270 $adapter->promise()
304 ->always(function () use ($exception) {
305 return \React\Promise\reject($exception);
271 ->finally(function () use ($exception) {
272 return reject($exception);
306273 })
307274 ->then(null, $mock);
308275
11
22 namespace React\Promise;
33
4 use Exception;
45 use React\Promise\PromiseAdapter\CallbackPromiseAdapter;
56
67 class PromiseTest extends TestCase
910
1011 public function getPromiseTestAdapter(callable $canceller = null)
1112 {
12 $resolveCallback = $rejectCallback = $progressCallback = null;
13
14 $promise = new Promise(function ($resolve, $reject, $progress) use (&$resolveCallback, &$rejectCallback, &$progressCallback) {
15 $resolveCallback = $resolve;
16 $rejectCallback = $reject;
17 $progressCallback = $progress;
13 $resolveCallback = $rejectCallback = null;
14
15 $promise = new Promise(function ($resolve, $reject) use (&$resolveCallback, &$rejectCallback) {
16 $resolveCallback = $resolve;
17 $rejectCallback = $reject;
1818 }, $canceller);
1919
2020 return new CallbackPromiseAdapter([
2323 },
2424 'resolve' => $resolveCallback,
2525 'reject' => $rejectCallback,
26 'notify' => $progressCallback,
2726 'settle' => $resolveCallback,
2827 ]);
2928 }
3130 /** @test */
3231 public function shouldRejectIfResolverThrowsException()
3332 {
34 $exception = new \Exception('foo');
33 $exception = new Exception('foo');
3534
3635 $promise = new Promise(function () use ($exception) {
3736 throw $exception;
3938
4039 $mock = $this->createCallableMock();
4140 $mock
42 ->expects($this->once())
41 ->expects(self::once())
4342 ->method('__invoke')
44 ->with($this->identicalTo($exception));
43 ->with(self::identicalTo($exception));
4544
4645 $promise
4746 ->then($this->expectCallableNever(), $mock);
5150 public function shouldResolveWithoutCreatingGarbageCyclesIfResolverResolvesWithException()
5251 {
5352 gc_collect_cycles();
54 gc_collect_cycles(); // clear twice to avoid leftovers in PHP 7.4 with ext-xdebug and code coverage turned on
55
5653 $promise = new Promise(function ($resolve) {
5754 $resolve(new \Exception('foo'));
5855 });
181178 }
182179
183180 /** @test */
184 public function shouldIgnoreNotifyAfterReject()
185 {
186 $promise = new Promise(function () { }, function ($resolve, $reject, $notify) {
187 $reject(new \Exception('foo'));
188 $notify(42);
189 });
190
191 $promise->then(null, null, $this->expectCallableNever());
192 $promise->cancel();
193 }
194
195
196 /** @test */
197181 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromise()
198182 {
199183 gc_collect_cycles();
215199 }
216200
217201 /** @test */
218 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithDoneFollowers()
219 {
220 gc_collect_cycles();
221 $promise = new Promise(function () { });
222 $promise->done();
223 unset($promise);
224
225 $this->assertSame(0, gc_collect_cycles());
226 }
227
228 /** @test */
202 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithCatchFollowers()
203 {
204 gc_collect_cycles();
205 $promise = new Promise(function () { });
206 $promise->catch(function () { });
207 unset($promise);
208
209 $this->assertSame(0, gc_collect_cycles());
210 }
211
212 /** @test */
213 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithFinallyFollowers()
214 {
215 gc_collect_cycles();
216 $promise = new Promise(function () { });
217 $promise->finally(function () { });
218 unset($promise);
219
220 $this->assertSame(0, gc_collect_cycles());
221 }
222
223 /**
224 * @test
225 * @deprecated
226 */
229227 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithOtherwiseFollowers()
230228 {
231229 gc_collect_cycles();
236234 $this->assertSame(0, gc_collect_cycles());
237235 }
238236
239 /** @test */
237 /**
238 * @test
239 * @deprecated
240 */
240241 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithAlwaysFollowers()
241242 {
242243 gc_collect_cycles();
261262 /** @test */
262263 public function shouldFulfillIfFullfilledWithSimplePromise()
263264 {
264 $adapter = $this->getPromiseTestAdapter();
265
266 $mock = $this->createCallableMock();
267 $mock
268 ->expects($this->once())
269 ->method('__invoke')
270 ->with($this->identicalTo('foo'));
271
272 $adapter->promise()
273 ->then($mock);
274
275 $adapter->resolve(new SimpleFulfilledTestPromise());
276 }
277
278 /** @test */
279 public function shouldRejectIfRejectedWithSimplePromise()
280 {
281 $adapter = $this->getPromiseTestAdapter();
282
283 $mock = $this->createCallableMock();
284 $mock
285 ->expects($this->once())
286 ->method('__invoke')
287 ->with($this->identicalTo('foo'));
288
289 $adapter->promise()
290 ->then($this->expectCallableNever(), $mock);
291
292 $adapter->resolve(new SimpleRejectedTestPromise());
265 gc_collect_cycles();
266 $promise = new Promise(function () {
267 throw new Exception('foo');
268 });
269 unset($promise);
270
271 self::assertSame(0, gc_collect_cycles());
293272 }
294273 }
+0
-76
tests/RejectedPromiseTest.php less more
0 <?php
1
2 namespace React\Promise;
3
4 use React\Promise\PromiseAdapter\CallbackPromiseAdapter;
5
6 class RejectedPromiseTest extends TestCase
7 {
8 use PromiseTest\PromiseSettledTestTrait,
9 PromiseTest\PromiseRejectedTestTrait;
10
11 public function getPromiseTestAdapter(callable $canceller = null)
12 {
13 $promise = null;
14
15 return new CallbackPromiseAdapter([
16 'promise' => function () use (&$promise) {
17 if (!$promise) {
18 throw new \LogicException('RejectedPromise must be rejected before obtaining the promise');
19 }
20
21 return $promise;
22 },
23 'resolve' => function () {
24 throw new \LogicException('You cannot call resolve() for React\Promise\RejectedPromise');
25 },
26 'reject' => function ($reason = null) use (&$promise) {
27 if (!$promise) {
28 $promise = new RejectedPromise($reason);
29 }
30 },
31 'notify' => function () {
32 // no-op
33 },
34 'settle' => function ($reason = null) use (&$promise) {
35 if (!$promise) {
36 $promise = new RejectedPromise($reason);
37 }
38 },
39 ]);
40 }
41
42 /** @test */
43 public function shouldThrowExceptionIfConstructedWithAPromise()
44 {
45 $this->setExpectedException('\InvalidArgumentException');
46
47 return new RejectedPromise(new RejectedPromise());
48 }
49
50 /** @test */
51 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToRejectedPromiseWithAlwaysFollowers()
52 {
53 gc_collect_cycles();
54 $promise = new RejectedPromise(1);
55 $promise->always(function () {
56 throw new \RuntimeException();
57 });
58 unset($promise);
59
60 $this->assertSame(0, gc_collect_cycles());
61 }
62
63 /** @test */
64 public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToRejectedPromiseWithThenFollowers()
65 {
66 gc_collect_cycles();
67 $promise = new RejectedPromise(1);
68 $promise = $promise->then(null, function () {
69 throw new \RuntimeException();
70 });
71 unset($promise);
72
73 $this->assertSame(0, gc_collect_cycles());
74 }
75 }
11
22 namespace React\Promise;
33
4 class TestCase extends \PHPUnit\Framework\TestCase
4 use PHPUnit\Framework\TestCase as BaseTestCase;
5
6 class TestCase extends BaseTestCase
57 {
6 public function expectCallableExactly($amount)
8 public function expectCallableExactly($amount): callable
79 {
810 $mock = $this->createCallableMock();
911 $mock
10 ->expects($this->exactly($amount))
12 ->expects(self::exactly($amount))
1113 ->method('__invoke');
1214
1315 return $mock;
1416 }
1517
16 public function expectCallableOnce()
18 public function expectCallableOnce(): callable
1719 {
1820 $mock = $this->createCallableMock();
1921 $mock
20 ->expects($this->once())
22 ->expects(self::once())
2123 ->method('__invoke');
2224
2325 return $mock;
2426 }
2527
26 public function expectCallableNever()
28 public function expectCallableNever(): callable
2729 {
2830 $mock = $this->createCallableMock();
2931 $mock
30 ->expects($this->never())
32 ->expects(self::never())
3133 ->method('__invoke');
3234
3335 return $mock;
3436 }
3537
36 public function createCallableMock()
38 protected function createCallableMock()
3739 {
3840 if (method_exists('PHPUnit\Framework\MockObject\MockBuilder', 'addMethods')) {
39 // PHPUnit 10+
41 // PHPUnit 9+
4042 return $this->getMockBuilder('stdClass')->addMethods(array('__invoke'))->getMock();
4143 } else {
4244 // legacy PHPUnit 4 - PHPUnit 9
4345 return $this->getMockBuilder('stdClass')->setMethods(array('__invoke'))->getMock();
4446 }
4547 }
46
47 public function setExpectedException($exception, $exceptionMessage = '', $exceptionCode = null)
48 {
49 if (method_exists($this, 'expectException')) {
50 // PHPUnit 5+
51 $this->expectException($exception);
52 if ($exceptionMessage !== '') {
53 $this->expectExceptionMessage($exceptionMessage);
54 }
55 if ($exceptionCode !== null) {
56 $this->expectExceptionCode($exceptionCode);
57 }
58 } else {
59 // legacy PHPUnit 4
60 parent::setExpectedException($exception, $exceptionMessage, $exceptionCode);
61 }
62 }
6348 }
0 <?php
1
2 namespace React\Promise;
3
4 use RuntimeException;
5
6 class ArrayAccessibleException extends RuntimeException implements \ArrayAccess
7 {
8 public function offsetExists(mixed $offset): bool
9 {
10 return true;
11 }
12
13 public function offsetGet(mixed $offset): mixed
14 {
15 return $offset;
16 }
17
18 public function offsetSet(mixed $offset, mixed $value): void {}
19
20 public function offsetUnset(mixed $offset): void {}
21 }
0 <?php
1
2 namespace React\Promise;
3
4 use Countable;
5 use RuntimeException;
6
7 class CallbackWithDNFTypehintClass
8 {
9 public function __invoke((RuntimeException&Countable)|(RuntimeException&\ArrayAccess) $e)
10 {
11 }
12
13 public function testCallback((RuntimeException&Countable)|(RuntimeException&\ArrayAccess) $e)
14 {
15 }
16
17 public static function testCallbackStatic((RuntimeException&Countable)|(RuntimeException&\ArrayAccess) $e)
18 {
19 }
20 }
66
77 class CountableException extends RuntimeException implements Countable
88 {
9 public function count()
9 public function count(): int
1010 {
1111 return 0;
1212 }
+0
-15
tests/fixtures/CountableNonException.php less more
0 <?php
1
2 namespace React\Promise;
3
4 use Countable;
5 use RuntimeException;
6
7 class CountableNonException implements Countable
8 {
9 public function count()
10 {
11 return 0;
12 }
13 }
14
+0
-21
tests/fixtures/SimpleFulfilledTestPromise.php less more
0 <?php
1
2 namespace React\Promise;
3
4 class SimpleFulfilledTestPromise implements PromiseInterface
5 {
6 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
7 {
8 try {
9 if ($onFulfilled) {
10 $onFulfilled('foo');
11 }
12
13 return new self();
14 } catch (\Throwable $exception) {
15 return new RejectedPromise($exception);
16 } catch (\Exception $exception) {
17 return new RejectedPromise($exception);
18 }
19 }
20 }
11
22 namespace React\Promise;
33
4 use React\Promise\Internal\RejectedPromise;
5
46 class SimpleFulfilledTestThenable
57 {
6 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
8 public function then(callable $onFulfilled = null, callable $onRejected = null)
79 {
810 try {
911 if ($onFulfilled) {
+0
-21
tests/fixtures/SimpleRejectedTestPromise.php less more
0 <?php
1
2 namespace React\Promise;
3
4 class SimpleRejectedTestPromise implements PromiseInterface
5 {
6 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
7 {
8 try {
9 if ($onRejected) {
10 $onRejected('foo');
11 }
12
13 return new self();
14 } catch (\Throwable $exception) {
15 return new RejectedPromise($exception);
16 } catch (\Exception $exception) {
17 return new RejectedPromise($exception);
18 }
19 }
20 }
44 class SimpleTestCancellableThenable
55 {
66 public $cancelCalled = false;
7 public $onCancel;
78
8 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
9 public function __construct(callable $onCancel = null)
10 {
11 $this->onCancel = $onCancel;
12 }
13
14 public function then(callable $onFulfilled = null, callable $onRejected = null)
915 {
1016 return new self();
1117 }
1319 public function cancel()
1420 {
1521 $this->cancelCalled = true;
22
23 if (is_callable($this->onCancel)) {
24 ($this->onCancel)();
25 }
1626 }
1727 }