PDF rausgenommen

This commit is contained in:
aschwarz
2023-01-23 11:03:31 +01:00
parent 82d562a322
commit a6523903eb
28078 changed files with 4247552 additions and 2 deletions

View File

@ -0,0 +1,22 @@
Copyright (c) 2012-2016 Jan Sorgalla
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,29 @@
{
"name": "react/promise",
"description": "A lightweight implementation of CommonJS Promises/A for PHP",
"license": "MIT",
"authors": [
{"name": "Jan Sorgalla", "email": "jsorgalla@gmail.com"}
],
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"autoload": {
"psr-4": {
"React\\Promise\\": "src/"
},
"files": ["src/functions_include.php"]
},
"autoload-dev": {
"psr-4": {
"React\\Promise\\": "tests/fixtures"
}
},
"keywords": [
"promise",
"promises"
]
}

View File

@ -0,0 +1,11 @@
<?php
namespace React\Promise;
interface CancellablePromiseInterface extends PromiseInterface
{
/**
* @return void
*/
public function cancel();
}

View File

@ -0,0 +1,55 @@
<?php
namespace React\Promise;
class CancellationQueue
{
private $started = false;
private $queue = [];
public function __invoke()
{
if ($this->started) {
return;
}
$this->started = true;
$this->drain();
}
public function enqueue($cancellable)
{
if (!method_exists($cancellable, 'then') || !method_exists($cancellable, 'cancel')) {
return;
}
$length = array_push($this->queue, $cancellable);
if ($this->started && 1 === $length) {
$this->drain();
}
}
private function drain()
{
for ($i = key($this->queue); isset($this->queue[$i]); $i++) {
$cancellable = $this->queue[$i];
$exception = null;
try {
$cancellable->cancel();
} catch (\Throwable $exception) {
} catch (\Exception $exception) {
}
unset($this->queue[$i]);
if ($exception) {
throw $exception;
}
}
$this->queue = [];
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace React\Promise;
class Deferred implements PromisorInterface
{
private $promise;
private $resolveCallback;
private $rejectCallback;
private $notifyCallback;
private $canceller;
public function __construct(callable $canceller = null)
{
$this->canceller = $canceller;
}
public function promise()
{
if (null === $this->promise) {
$this->promise = new Promise(function ($resolve, $reject, $notify) {
$this->resolveCallback = $resolve;
$this->rejectCallback = $reject;
$this->notifyCallback = $notify;
}, $this->canceller);
$this->canceller = null;
}
return $this->promise;
}
public function resolve($value = null)
{
$this->promise();
call_user_func($this->resolveCallback, $value);
}
public function reject($reason = null)
{
$this->promise();
call_user_func($this->rejectCallback, $reason);
}
/**
* @deprecated 2.6.0 Progress support is deprecated and should not be used anymore.
* @param mixed $update
*/
public function notify($update = null)
{
$this->promise();
call_user_func($this->notifyCallback, $update);
}
/**
* @deprecated 2.2.0
* @see Deferred::notify()
*/
public function progress($update = null)
{
$this->notify($update);
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace React\Promise\Exception;
class LengthException extends \LengthException
{
}

View File

@ -0,0 +1,30 @@
<?php
namespace React\Promise;
interface ExtendedPromiseInterface extends PromiseInterface
{
/**
*
* The `$onProgress` argument is deprecated and should not be used anymore.
*
* @return void
*/
public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null);
/**
* @return ExtendedPromiseInterface
*/
public function otherwise(callable $onRejected);
/**
* @return ExtendedPromiseInterface
*/
public function always(callable $onFulfilledOrRejected);
/**
* @return ExtendedPromiseInterface
* @deprecated 2.6.0 Progress support is deprecated and should not be used anymore.
*/
public function progress(callable $onProgress);
}

View File

@ -0,0 +1,68 @@
<?php
namespace React\Promise;
class FulfilledPromise implements ExtendedPromiseInterface, CancellablePromiseInterface
{
private $value;
public function __construct($value = null)
{
if ($value instanceof PromiseInterface) {
throw new \InvalidArgumentException('You cannot create React\Promise\FulfilledPromise with a promise. Use React\Promise\resolve($promiseOrValue) instead.');
}
$this->value = $value;
}
public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
{
if (null === $onFulfilled) {
return $this;
}
try {
return resolve($onFulfilled($this->value));
} catch (\Throwable $exception) {
return new RejectedPromise($exception);
} catch (\Exception $exception) {
return new RejectedPromise($exception);
}
}
public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
{
if (null === $onFulfilled) {
return;
}
$result = $onFulfilled($this->value);
if ($result instanceof ExtendedPromiseInterface) {
$result->done();
}
}
public function otherwise(callable $onRejected)
{
return $this;
}
public function always(callable $onFulfilledOrRejected)
{
return $this->then(function ($value) use ($onFulfilledOrRejected) {
return resolve($onFulfilledOrRejected())->then(function () use ($value) {
return $value;
});
});
}
public function progress(callable $onProgress)
{
return $this;
}
public function cancel()
{
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace React\Promise;
class LazyPromise implements ExtendedPromiseInterface, CancellablePromiseInterface
{
private $factory;
private $promise;
public function __construct(callable $factory)
{
$this->factory = $factory;
}
public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
{
return $this->promise()->then($onFulfilled, $onRejected, $onProgress);
}
public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
{
return $this->promise()->done($onFulfilled, $onRejected, $onProgress);
}
public function otherwise(callable $onRejected)
{
return $this->promise()->otherwise($onRejected);
}
public function always(callable $onFulfilledOrRejected)
{
return $this->promise()->always($onFulfilledOrRejected);
}
public function progress(callable $onProgress)
{
return $this->promise()->progress($onProgress);
}
public function cancel()
{
return $this->promise()->cancel();
}
/**
* @internal
* @see Promise::settle()
*/
public function promise()
{
if (null === $this->promise) {
try {
$this->promise = resolve(call_user_func($this->factory));
} catch (\Throwable $exception) {
$this->promise = new RejectedPromise($exception);
} catch (\Exception $exception) {
$this->promise = new RejectedPromise($exception);
}
}
return $this->promise;
}
}

View File

@ -0,0 +1,256 @@
<?php
namespace React\Promise;
class Promise implements ExtendedPromiseInterface, CancellablePromiseInterface
{
private $canceller;
private $result;
private $handlers = [];
private $progressHandlers = [];
private $requiredCancelRequests = 0;
private $cancelRequests = 0;
public function __construct(callable $resolver, callable $canceller = null)
{
$this->canceller = $canceller;
// Explicitly overwrite arguments with null values before invoking
// resolver function. This ensure that these arguments do not show up
// in the stack trace in PHP 7+ only.
$cb = $resolver;
$resolver = $canceller = null;
$this->call($cb);
}
public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
{
if (null !== $this->result) {
return $this->result->then($onFulfilled, $onRejected, $onProgress);
}
if (null === $this->canceller) {
return new static($this->resolver($onFulfilled, $onRejected, $onProgress));
}
// This promise has a canceller, so we create a new child promise which
// has a canceller that invokes the parent canceller if all other
// followers are also cancelled. We keep a reference to this promise
// instance for the static canceller function and clear this to avoid
// keeping a cyclic reference between parent and follower.
$parent = $this;
++$parent->requiredCancelRequests;
return new static(
$this->resolver($onFulfilled, $onRejected, $onProgress),
static function () use (&$parent) {
if (++$parent->cancelRequests >= $parent->requiredCancelRequests) {
$parent->cancel();
}
$parent = null;
}
);
}
public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
{
if (null !== $this->result) {
return $this->result->done($onFulfilled, $onRejected, $onProgress);
}
$this->handlers[] = static function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected) {
$promise
->done($onFulfilled, $onRejected);
};
if ($onProgress) {
$this->progressHandlers[] = $onProgress;
}
}
public function otherwise(callable $onRejected)
{
return $this->then(null, static function ($reason) use ($onRejected) {
if (!_checkTypehint($onRejected, $reason)) {
return new RejectedPromise($reason);
}
return $onRejected($reason);
});
}
public function always(callable $onFulfilledOrRejected)
{
return $this->then(static function ($value) use ($onFulfilledOrRejected) {
return resolve($onFulfilledOrRejected())->then(function () use ($value) {
return $value;
});
}, static function ($reason) use ($onFulfilledOrRejected) {
return resolve($onFulfilledOrRejected())->then(function () use ($reason) {
return new RejectedPromise($reason);
});
});
}
public function progress(callable $onProgress)
{
return $this->then(null, null, $onProgress);
}
public function cancel()
{
if (null === $this->canceller || null !== $this->result) {
return;
}
$canceller = $this->canceller;
$this->canceller = null;
$this->call($canceller);
}
private function resolver(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
{
return function ($resolve, $reject, $notify) use ($onFulfilled, $onRejected, $onProgress) {
if ($onProgress) {
$progressHandler = static function ($update) use ($notify, $onProgress) {
try {
$notify($onProgress($update));
} catch (\Throwable $e) {
$notify($e);
} catch (\Exception $e) {
$notify($e);
}
};
} else {
$progressHandler = $notify;
}
$this->handlers[] = static function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject, $progressHandler) {
$promise
->then($onFulfilled, $onRejected)
->done($resolve, $reject, $progressHandler);
};
$this->progressHandlers[] = $progressHandler;
};
}
private function reject($reason = null)
{
if (null !== $this->result) {
return;
}
$this->settle(reject($reason));
}
private function settle(ExtendedPromiseInterface $promise)
{
$promise = $this->unwrap($promise);
if ($promise === $this) {
$promise = new RejectedPromise(
new \LogicException('Cannot resolve a promise with itself.')
);
}
$handlers = $this->handlers;
$this->progressHandlers = $this->handlers = [];
$this->result = $promise;
$this->canceller = null;
foreach ($handlers as $handler) {
$handler($promise);
}
}
private function unwrap($promise)
{
$promise = $this->extract($promise);
while ($promise instanceof self && null !== $promise->result) {
$promise = $this->extract($promise->result);
}
return $promise;
}
private function extract($promise)
{
if ($promise instanceof LazyPromise) {
$promise = $promise->promise();
}
return $promise;
}
private function call(callable $cb)
{
// Explicitly overwrite argument with null value. This ensure that this
// argument does not show up in the stack trace in PHP 7+ only.
$callback = $cb;
$cb = null;
// Use reflection to inspect number of arguments expected by this callback.
// We did some careful benchmarking here: Using reflection to avoid unneeded
// function arguments is actually faster than blindly passing them.
// Also, this helps avoiding unnecessary function arguments in the call stack
// if the callback creates an Exception (creating garbage cycles).
if (is_array($callback)) {
$ref = new \ReflectionMethod($callback[0], $callback[1]);
} elseif (is_object($callback) && !$callback instanceof \Closure) {
$ref = new \ReflectionMethod($callback, '__invoke');
} else {
$ref = new \ReflectionFunction($callback);
}
$args = $ref->getNumberOfParameters();
try {
if ($args === 0) {
$callback();
} else {
// Keep references to this promise instance for the static resolve/reject functions.
// By using static callbacks that are not bound to this instance
// and passing the target promise instance by reference, we can
// still execute its resolving logic and still clear this
// reference when settling the promise. This helps avoiding
// garbage cycles if any callback creates an Exception.
// These assumptions are covered by the test suite, so if you ever feel like
// refactoring this, go ahead, any alternative suggestions are welcome!
$target =& $this;
$progressHandlers =& $this->progressHandlers;
$callback(
static function ($value = null) use (&$target) {
if ($target !== null) {
$target->settle(resolve($value));
$target = null;
}
},
static function ($reason = null) use (&$target) {
if ($target !== null) {
$target->reject($reason);
$target = null;
}
},
static function ($update = null) use (&$progressHandlers) {
foreach ($progressHandlers as $handler) {
$handler($update);
}
}
);
}
} catch (\Throwable $e) {
$target = null;
$this->reject($e);
} catch (\Exception $e) {
$target = null;
$this->reject($e);
}
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace React\Promise;
interface PromiseInterface
{
/**
*
* The `$onProgress` argument is deprecated and should not be used anymore.
*
* @return PromiseInterface
*/
public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null);
}

View File

@ -0,0 +1,11 @@
<?php
namespace React\Promise;
interface PromisorInterface
{
/**
* @return PromiseInterface
*/
public function promise();
}

View File

@ -0,0 +1,76 @@
<?php
namespace React\Promise;
class RejectedPromise implements ExtendedPromiseInterface, CancellablePromiseInterface
{
private $reason;
public function __construct($reason = null)
{
if ($reason instanceof PromiseInterface) {
throw new \InvalidArgumentException('You cannot create React\Promise\RejectedPromise with a promise. Use React\Promise\reject($promiseOrValue) instead.');
}
$this->reason = $reason;
}
public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
{
if (null === $onRejected) {
return $this;
}
try {
return resolve($onRejected($this->reason));
} catch (\Throwable $exception) {
return new RejectedPromise($exception);
} catch (\Exception $exception) {
return new RejectedPromise($exception);
}
}
public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
{
if (null === $onRejected) {
throw UnhandledRejectionException::resolve($this->reason);
}
$result = $onRejected($this->reason);
if ($result instanceof self) {
throw UnhandledRejectionException::resolve($result->reason);
}
if ($result instanceof ExtendedPromiseInterface) {
$result->done();
}
}
public function otherwise(callable $onRejected)
{
if (!_checkTypehint($onRejected, $this->reason)) {
return $this;
}
return $this->then(null, $onRejected);
}
public function always(callable $onFulfilledOrRejected)
{
return $this->then(null, function ($reason) use ($onFulfilledOrRejected) {
return resolve($onFulfilledOrRejected())->then(function () use ($reason) {
return new RejectedPromise($reason);
});
});
}
public function progress(callable $onProgress)
{
return $this;
}
public function cancel()
{
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace React\Promise;
class UnhandledRejectionException extends \RuntimeException
{
private $reason;
public static function resolve($reason)
{
if ($reason instanceof \Exception || $reason instanceof \Throwable) {
return $reason;
}
return new static($reason);
}
public function __construct($reason)
{
$this->reason = $reason;
$message = sprintf('Unhandled Rejection: %s', json_encode($reason));
parent::__construct($message, 0);
}
public function getReason()
{
return $this->reason;
}
}

View File

@ -0,0 +1,244 @@
<?php
namespace React\Promise;
function resolve($promiseOrValue = null)
{
if ($promiseOrValue instanceof ExtendedPromiseInterface) {
return $promiseOrValue;
}
if (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);
}

View File

@ -0,0 +1,5 @@
<?php
if (!function_exists('React\Promise\resolve')) {
require __DIR__.'/functions.php';
}