Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
namespace React\Promise;
4
 
5
/**
6
 * Creates a promise for the supplied `$promiseOrValue`.
7
 *
8
 * If `$promiseOrValue` is a value, it will be the resolution value of the
9
 * returned promise.
10
 *
11
 * If `$promiseOrValue` is a thenable (any object that provides a `then()` method),
12
 * a trusted promise that follows the state of the thenable is returned.
13
 *
14
 * If `$promiseOrValue` is a promise, it will be returned as is.
15
 *
16
 * @param mixed $promiseOrValue
17
 * @return PromiseInterface
18
 */
19
function resolve($promiseOrValue = null)
20
{
21
    if ($promiseOrValue instanceof ExtendedPromiseInterface) {
22
        return $promiseOrValue;
23
    }
24
 
25
    // Check is_object() first to avoid method_exists() triggering
26
    // class autoloaders if $promiseOrValue is a string.
27
    if (\is_object($promiseOrValue) && \method_exists($promiseOrValue, 'then')) {
28
        $canceller = null;
29
 
30
        if (\method_exists($promiseOrValue, 'cancel')) {
31
            $canceller = [$promiseOrValue, 'cancel'];
32
        }
33
 
34
        return new Promise(function ($resolve, $reject, $notify) use ($promiseOrValue) {
35
            $promiseOrValue->then($resolve, $reject, $notify);
36
        }, $canceller);
37
    }
38
 
39
    return new FulfilledPromise($promiseOrValue);
40
}
41
 
42
/**
43
 * Creates a rejected promise for the supplied `$promiseOrValue`.
44
 *
45
 * If `$promiseOrValue` is a value, it will be the rejection value of the
46
 * returned promise.
47
 *
48
 * If `$promiseOrValue` is a promise, its completion value will be the rejected
49
 * value of the returned promise.
50
 *
51
 * This can be useful in situations where you need to reject a promise without
52
 * throwing an exception. For example, it allows you to propagate a rejection with
53
 * the value of another promise.
54
 *
55
 * @param mixed $promiseOrValue
56
 * @return PromiseInterface
57
 */
58
function reject($promiseOrValue = null)
59
{
60
    if ($promiseOrValue instanceof PromiseInterface) {
61
        return resolve($promiseOrValue)->then(function ($value) {
62
            return new RejectedPromise($value);
63
        });
64
    }
65
 
66
    return new RejectedPromise($promiseOrValue);
67
}
68
 
69
/**
70
 * Returns a promise that will resolve only once all the items in
71
 * `$promisesOrValues` have resolved. The resolution value of the returned promise
72
 * will be an array containing the resolution values of each of the items in
73
 * `$promisesOrValues`.
74
 *
75
 * @param array $promisesOrValues
76
 * @return PromiseInterface
77
 */
78
function all($promisesOrValues)
79
{
80
    return map($promisesOrValues, function ($val) {
81
        return $val;
82
    });
83
}
84
 
85
/**
86
 * Initiates a competitive race that allows one winner. Returns a promise which is
87
 * resolved in the same way the first settled promise resolves.
88
 *
89
 * The returned promise will become **infinitely pending** if  `$promisesOrValues`
90
 * contains 0 items.
91
 *
92
 * @param array $promisesOrValues
93
 * @return PromiseInterface
94
 */
95
function race($promisesOrValues)
96
{
97
    $cancellationQueue = new CancellationQueue();
98
    $cancellationQueue->enqueue($promisesOrValues);
99
 
100
    return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $cancellationQueue) {
101
        resolve($promisesOrValues)
102
            ->done(function ($array) use ($cancellationQueue, $resolve, $reject, $notify) {
103
                if (!is_array($array) || !$array) {
104
                    $resolve();
105
                    return;
106
                }
107
 
108
                foreach ($array as $promiseOrValue) {
109
                    $cancellationQueue->enqueue($promiseOrValue);
110
 
111
                    resolve($promiseOrValue)
112
                        ->done($resolve, $reject, $notify);
113
                }
114
            }, $reject, $notify);
115
    }, $cancellationQueue);
116
}
117
 
118
/**
119
 * Returns a promise that will resolve when any one of the items in
120
 * `$promisesOrValues` resolves. The resolution value of the returned promise
121
 * will be the resolution value of the triggering item.
122
 *
123
 * The returned promise will only reject if *all* items in `$promisesOrValues` are
124
 * rejected. The rejection value will be an array of all rejection reasons.
125
 *
126
 * The returned promise will also reject with a `React\Promise\Exception\LengthException`
127
 * if `$promisesOrValues` contains 0 items.
128
 *
129
 * @param array $promisesOrValues
130
 * @return PromiseInterface
131
 */
132
function any($promisesOrValues)
133
{
134
    return some($promisesOrValues, 1)
135
        ->then(function ($val) {
136
            return \array_shift($val);
137
        });
138
}
139
 
140
/**
141
 * Returns a promise that will resolve when `$howMany` of the supplied items in
142
 * `$promisesOrValues` resolve. The resolution value of the returned promise
143
 * will be an array of length `$howMany` containing the resolution values of the
144
 * triggering items.
145
 *
146
 * The returned promise will reject if it becomes impossible for `$howMany` items
147
 * to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items
148
 * reject). The rejection value will be an array of
149
 * `(count($promisesOrValues) - $howMany) + 1` rejection reasons.
150
 *
151
 * The returned promise will also reject with a `React\Promise\Exception\LengthException`
152
 * if `$promisesOrValues` contains less items than `$howMany`.
153
 *
154
 * @param array $promisesOrValues
155
 * @param int $howMany
156
 * @return PromiseInterface
157
 */
158
function some($promisesOrValues, $howMany)
159
{
160
    $cancellationQueue = new CancellationQueue();
161
    $cancellationQueue->enqueue($promisesOrValues);
162
 
163
    return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $howMany, $cancellationQueue) {
164
        resolve($promisesOrValues)
165
            ->done(function ($array) use ($howMany, $cancellationQueue, $resolve, $reject, $notify) {
166
                if (!\is_array($array) || $howMany < 1) {
167
                    $resolve([]);
168
                    return;
169
                }
170
 
171
                $len = \count($array);
172
 
173
                if ($len < $howMany) {
174
                    throw new Exception\LengthException(
175
                        \sprintf(
176
                            'Input array must contain at least %d item%s but contains only %s item%s.',
177
                            $howMany,
178
                            1 === $howMany ? '' : 's',
179
                            $len,
180
                            1 === $len ? '' : 's'
181
                        )
182
                    );
183
                }
184
 
185
                $toResolve = $howMany;
186
                $toReject  = ($len - $toResolve) + 1;
187
                $values    = [];
188
                $reasons   = [];
189
 
190
                foreach ($array as $i => $promiseOrValue) {
191
                    $fulfiller = function ($val) use ($i, &$values, &$toResolve, $toReject, $resolve) {
192
                        if ($toResolve < 1 || $toReject < 1) {
193
                            return;
194
                        }
195
 
196
                        $values[$i] = $val;
197
 
198
                        if (0 === --$toResolve) {
199
                            $resolve($values);
200
                        }
201
                    };
202
 
203
                    $rejecter = function ($reason) use ($i, &$reasons, &$toReject, $toResolve, $reject) {
204
                        if ($toResolve < 1 || $toReject < 1) {
205
                            return;
206
                        }
207
 
208
                        $reasons[$i] = $reason;
209
 
210
                        if (0 === --$toReject) {
211
                            $reject($reasons);
212
                        }
213
                    };
214
 
215
                    $cancellationQueue->enqueue($promiseOrValue);
216
 
217
                    resolve($promiseOrValue)
218
                        ->done($fulfiller, $rejecter, $notify);
219
                }
220
            }, $reject, $notify);
221
    }, $cancellationQueue);
222
}
223
 
224
/**
225
 * Traditional map function, similar to `array_map()`, but allows input to contain
226
 * promises and/or values, and `$mapFunc` may return either a value or a promise.
227
 *
228
 * The map function receives each item as argument, where item is a fully resolved
229
 * value of a promise or value in `$promisesOrValues`.
230
 *
231
 * @param array $promisesOrValues
232
 * @param callable $mapFunc
233
 * @return PromiseInterface
234
 */
235
function map($promisesOrValues, callable $mapFunc)
236
{
237
    $cancellationQueue = new CancellationQueue();
238
    $cancellationQueue->enqueue($promisesOrValues);
239
 
240
    return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $mapFunc, $cancellationQueue) {
241
        resolve($promisesOrValues)
242
            ->done(function ($array) use ($mapFunc, $cancellationQueue, $resolve, $reject, $notify) {
243
                if (!\is_array($array) || !$array) {
244
                    $resolve([]);
245
                    return;
246
                }
247
 
248
                $toResolve = \count($array);
249
                $values    = [];
250
 
251
                foreach ($array as $i => $promiseOrValue) {
252
                    $cancellationQueue->enqueue($promiseOrValue);
253
                    $values[$i] = null;
254
 
255
                    resolve($promiseOrValue)
256
                        ->then($mapFunc)
257
                        ->done(
258
                            function ($mapped) use ($i, &$values, &$toResolve, $resolve) {
259
                                $values[$i] = $mapped;
260
 
261
                                if (0 === --$toResolve) {
262
                                    $resolve($values);
263
                                }
264
                            },
265
                            $reject,
266
                            $notify
267
                        );
268
                }
269
            }, $reject, $notify);
270
    }, $cancellationQueue);
271
}
272
 
273
/**
274
 * Traditional reduce function, similar to `array_reduce()`, but input may contain
275
 * promises and/or values, and `$reduceFunc` may return either a value or a
276
 * promise, *and* `$initialValue` may be a promise or a value for the starting
277
 * value.
278
 *
279
 * @param array $promisesOrValues
280
 * @param callable $reduceFunc
281
 * @param mixed $initialValue
282
 * @return PromiseInterface
283
 */
284
function reduce($promisesOrValues, callable $reduceFunc, $initialValue = null)
285
{
286
    $cancellationQueue = new CancellationQueue();
287
    $cancellationQueue->enqueue($promisesOrValues);
288
 
289
    return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $reduceFunc, $initialValue, $cancellationQueue) {
290
        resolve($promisesOrValues)
291
            ->done(function ($array) use ($reduceFunc, $initialValue, $cancellationQueue, $resolve, $reject, $notify) {
292
                if (!\is_array($array)) {
293
                    $array = [];
294
                }
295
 
296
                $total = \count($array);
297
                $i = 0;
298
 
299
                // Wrap the supplied $reduceFunc with one that handles promises and then
300
                // delegates to the supplied.
301
                $wrappedReduceFunc = function ($current, $val) use ($reduceFunc, $cancellationQueue, $total, &$i) {
302
                    $cancellationQueue->enqueue($val);
303
 
304
                    return $current
305
                        ->then(function ($c) use ($reduceFunc, $total, &$i, $val) {
306
                            return resolve($val)
307
                                ->then(function ($value) use ($reduceFunc, $total, &$i, $c) {
308
                                    return $reduceFunc($c, $value, $i++, $total);
309
                                });
310
                        });
311
                };
312
 
313
                $cancellationQueue->enqueue($initialValue);
314
 
315
                \array_reduce($array, $wrappedReduceFunc, resolve($initialValue))
316
                    ->done($resolve, $reject, $notify);
317
            }, $reject, $notify);
318
    }, $cancellationQueue);
319
}
320
 
321
/**
322
 * @internal
323
 */
324
function _checkTypehint(callable $callback, $object)
325
{
326
    if (!\is_object($object)) {
327
        return true;
328
    }
329
 
330
    if (\is_array($callback)) {
331
        $callbackReflection = new \ReflectionMethod($callback[0], $callback[1]);
332
    } elseif (\is_object($callback) && !$callback instanceof \Closure) {
333
        $callbackReflection = new \ReflectionMethod($callback, '__invoke');
334
    } else {
335
        $callbackReflection = new \ReflectionFunction($callback);
336
    }
337
 
338
    $parameters = $callbackReflection->getParameters();
339
 
340
    if (!isset($parameters[0])) {
341
        return true;
342
    }
343
 
344
    $expectedException = $parameters[0];
345
 
346
    if (!$expectedException->getClass()) {
347
        return true;
348
    }
349
 
350
    return $expectedException->getClass()->isInstance($object);
351
}