247 lines
8.2 KiB
PHP
247 lines
8.2 KiB
PHP
<?php
|
|
|
|
namespace React\Promise;
|
|
|
|
function resolve($promiseOrValue = null)
|
|
{
|
|
if ($promiseOrValue instanceof ExtendedPromiseInterface) {
|
|
return $promiseOrValue;
|
|
}
|
|
|
|
// Check is_object() first to avoid method_exists() triggering
|
|
// class autoloaders if $promiseOrValue is a string.
|
|
if (\is_object($promiseOrValue) && \method_exists($promiseOrValue, 'then')) {
|
|
$canceller = null;
|
|
|
|
if (\method_exists($promiseOrValue, 'cancel')) {
|
|
$canceller = [$promiseOrValue, 'cancel'];
|
|
}
|
|
|
|
return new Promise(function ($resolve, $reject, $notify) use ($promiseOrValue) {
|
|
$promiseOrValue->then($resolve, $reject, $notify);
|
|
}, $canceller);
|
|
}
|
|
|
|
return new FulfilledPromise($promiseOrValue);
|
|
}
|
|
|
|
function reject($promiseOrValue = null)
|
|
{
|
|
if ($promiseOrValue instanceof PromiseInterface) {
|
|
return resolve($promiseOrValue)->then(function ($value) {
|
|
return new RejectedPromise($value);
|
|
});
|
|
}
|
|
|
|
return new RejectedPromise($promiseOrValue);
|
|
}
|
|
|
|
function all($promisesOrValues)
|
|
{
|
|
return map($promisesOrValues, function ($val) {
|
|
return $val;
|
|
});
|
|
}
|
|
|
|
function race($promisesOrValues)
|
|
{
|
|
$cancellationQueue = new CancellationQueue();
|
|
$cancellationQueue->enqueue($promisesOrValues);
|
|
|
|
return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $cancellationQueue) {
|
|
resolve($promisesOrValues)
|
|
->done(function ($array) use ($cancellationQueue, $resolve, $reject, $notify) {
|
|
if (!is_array($array) || !$array) {
|
|
$resolve();
|
|
return;
|
|
}
|
|
|
|
foreach ($array as $promiseOrValue) {
|
|
$cancellationQueue->enqueue($promiseOrValue);
|
|
|
|
resolve($promiseOrValue)
|
|
->done($resolve, $reject, $notify);
|
|
}
|
|
}, $reject, $notify);
|
|
}, $cancellationQueue);
|
|
}
|
|
|
|
function any($promisesOrValues)
|
|
{
|
|
return some($promisesOrValues, 1)
|
|
->then(function ($val) {
|
|
return \array_shift($val);
|
|
});
|
|
}
|
|
|
|
function some($promisesOrValues, $howMany)
|
|
{
|
|
$cancellationQueue = new CancellationQueue();
|
|
$cancellationQueue->enqueue($promisesOrValues);
|
|
|
|
return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $howMany, $cancellationQueue) {
|
|
resolve($promisesOrValues)
|
|
->done(function ($array) use ($howMany, $cancellationQueue, $resolve, $reject, $notify) {
|
|
if (!\is_array($array) || $howMany < 1) {
|
|
$resolve([]);
|
|
return;
|
|
}
|
|
|
|
$len = \count($array);
|
|
|
|
if ($len < $howMany) {
|
|
throw new Exception\LengthException(
|
|
\sprintf(
|
|
'Input array must contain at least %d item%s but contains only %s item%s.',
|
|
$howMany,
|
|
1 === $howMany ? '' : 's',
|
|
$len,
|
|
1 === $len ? '' : 's'
|
|
)
|
|
);
|
|
}
|
|
|
|
$toResolve = $howMany;
|
|
$toReject = ($len - $toResolve) + 1;
|
|
$values = [];
|
|
$reasons = [];
|
|
|
|
foreach ($array as $i => $promiseOrValue) {
|
|
$fulfiller = function ($val) use ($i, &$values, &$toResolve, $toReject, $resolve) {
|
|
if ($toResolve < 1 || $toReject < 1) {
|
|
return;
|
|
}
|
|
|
|
$values[$i] = $val;
|
|
|
|
if (0 === --$toResolve) {
|
|
$resolve($values);
|
|
}
|
|
};
|
|
|
|
$rejecter = function ($reason) use ($i, &$reasons, &$toReject, $toResolve, $reject) {
|
|
if ($toResolve < 1 || $toReject < 1) {
|
|
return;
|
|
}
|
|
|
|
$reasons[$i] = $reason;
|
|
|
|
if (0 === --$toReject) {
|
|
$reject($reasons);
|
|
}
|
|
};
|
|
|
|
$cancellationQueue->enqueue($promiseOrValue);
|
|
|
|
resolve($promiseOrValue)
|
|
->done($fulfiller, $rejecter, $notify);
|
|
}
|
|
}, $reject, $notify);
|
|
}, $cancellationQueue);
|
|
}
|
|
|
|
function map($promisesOrValues, callable $mapFunc)
|
|
{
|
|
$cancellationQueue = new CancellationQueue();
|
|
$cancellationQueue->enqueue($promisesOrValues);
|
|
|
|
return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $mapFunc, $cancellationQueue) {
|
|
resolve($promisesOrValues)
|
|
->done(function ($array) use ($mapFunc, $cancellationQueue, $resolve, $reject, $notify) {
|
|
if (!\is_array($array) || !$array) {
|
|
$resolve([]);
|
|
return;
|
|
}
|
|
|
|
$toResolve = \count($array);
|
|
$values = [];
|
|
|
|
foreach ($array as $i => $promiseOrValue) {
|
|
$cancellationQueue->enqueue($promiseOrValue);
|
|
$values[$i] = null;
|
|
|
|
resolve($promiseOrValue)
|
|
->then($mapFunc)
|
|
->done(
|
|
function ($mapped) use ($i, &$values, &$toResolve, $resolve) {
|
|
$values[$i] = $mapped;
|
|
|
|
if (0 === --$toResolve) {
|
|
$resolve($values);
|
|
}
|
|
},
|
|
$reject,
|
|
$notify
|
|
);
|
|
}
|
|
}, $reject, $notify);
|
|
}, $cancellationQueue);
|
|
}
|
|
|
|
function reduce($promisesOrValues, callable $reduceFunc, $initialValue = null)
|
|
{
|
|
$cancellationQueue = new CancellationQueue();
|
|
$cancellationQueue->enqueue($promisesOrValues);
|
|
|
|
return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $reduceFunc, $initialValue, $cancellationQueue) {
|
|
resolve($promisesOrValues)
|
|
->done(function ($array) use ($reduceFunc, $initialValue, $cancellationQueue, $resolve, $reject, $notify) {
|
|
if (!\is_array($array)) {
|
|
$array = [];
|
|
}
|
|
|
|
$total = \count($array);
|
|
$i = 0;
|
|
|
|
// Wrap the supplied $reduceFunc with one that handles promises and then
|
|
// delegates to the supplied.
|
|
$wrappedReduceFunc = function ($current, $val) use ($reduceFunc, $cancellationQueue, $total, &$i) {
|
|
$cancellationQueue->enqueue($val);
|
|
|
|
return $current
|
|
->then(function ($c) use ($reduceFunc, $total, &$i, $val) {
|
|
return resolve($val)
|
|
->then(function ($value) use ($reduceFunc, $total, &$i, $c) {
|
|
return $reduceFunc($c, $value, $i++, $total);
|
|
});
|
|
});
|
|
};
|
|
|
|
$cancellationQueue->enqueue($initialValue);
|
|
|
|
\array_reduce($array, $wrappedReduceFunc, resolve($initialValue))
|
|
->done($resolve, $reject, $notify);
|
|
}, $reject, $notify);
|
|
}, $cancellationQueue);
|
|
}
|
|
|
|
// Internal functions
|
|
function _checkTypehint(callable $callback, $object)
|
|
{
|
|
if (!\is_object($object)) {
|
|
return true;
|
|
}
|
|
|
|
if (\is_array($callback)) {
|
|
$callbackReflection = new \ReflectionMethod($callback[0], $callback[1]);
|
|
} elseif (\is_object($callback) && !$callback instanceof \Closure) {
|
|
$callbackReflection = new \ReflectionMethod($callback, '__invoke');
|
|
} else {
|
|
$callbackReflection = new \ReflectionFunction($callback);
|
|
}
|
|
|
|
$parameters = $callbackReflection->getParameters();
|
|
|
|
if (!isset($parameters[0])) {
|
|
return true;
|
|
}
|
|
|
|
$expectedException = $parameters[0];
|
|
|
|
if (!$expectedException->getClass()) {
|
|
return true;
|
|
}
|
|
|
|
return $expectedException->getClass()->isInstance($object);
|
|
}
|