summaryrefslogtreecommitdiff
path: root/server/vendor/guzzlehttp/promises
diff options
context:
space:
mode:
authorEole <josselin.35@live.fr>2016-01-21 10:29:26 +0100
committerEole <josselin.35@live.fr>2016-01-21 10:29:26 +0100
commita44cc1d2e3c0f147e91a5c052ac7fd879e34e706 (patch)
treebdd6f72e0ba732c4fcc0479d1cfcf4d0baa5885d /server/vendor/guzzlehttp/promises
parent35db27b0e62b4cdcb03b0d21bceb4efc769e6161 (diff)
Init Server Composer Components
Diffstat (limited to 'server/vendor/guzzlehttp/promises')
-rw-r--r--server/vendor/guzzlehttp/promises/.gitignore11
-rw-r--r--server/vendor/guzzlehttp/promises/.travis.yml19
-rw-r--r--server/vendor/guzzlehttp/promises/CHANGELOG.md21
-rw-r--r--server/vendor/guzzlehttp/promises/LICENSE19
-rw-r--r--server/vendor/guzzlehttp/promises/Makefile13
-rw-r--r--server/vendor/guzzlehttp/promises/README.md501
-rw-r--r--server/vendor/guzzlehttp/promises/composer.json31
-rw-r--r--server/vendor/guzzlehttp/promises/phpunit.xml.dist17
-rw-r--r--server/vendor/guzzlehttp/promises/src/AggregateException.php16
-rw-r--r--server/vendor/guzzlehttp/promises/src/CancellationException.php9
-rw-r--r--server/vendor/guzzlehttp/promises/src/EachPromise.php207
-rw-r--r--server/vendor/guzzlehttp/promises/src/FulfilledPromise.php80
-rw-r--r--server/vendor/guzzlehttp/promises/src/Promise.php268
-rw-r--r--server/vendor/guzzlehttp/promises/src/PromiseInterface.php93
-rw-r--r--server/vendor/guzzlehttp/promises/src/PromisorInterface.php15
-rw-r--r--server/vendor/guzzlehttp/promises/src/RejectedPromise.php84
-rw-r--r--server/vendor/guzzlehttp/promises/src/RejectionException.php47
-rw-r--r--server/vendor/guzzlehttp/promises/src/TaskQueue.php79
-rw-r--r--server/vendor/guzzlehttp/promises/src/functions.php495
-rw-r--r--server/vendor/guzzlehttp/promises/src/functions_include.php6
-rw-r--r--server/vendor/guzzlehttp/promises/tests/AggregateExceptionTest.php14
-rw-r--r--server/vendor/guzzlehttp/promises/tests/EachPromiseTest.php294
-rw-r--r--server/vendor/guzzlehttp/promises/tests/FulfilledPromiseTest.php108
-rw-r--r--server/vendor/guzzlehttp/promises/tests/NotPromiseInstance.php50
-rw-r--r--server/vendor/guzzlehttp/promises/tests/PromiseTest.php579
-rw-r--r--server/vendor/guzzlehttp/promises/tests/RejectedPromiseTest.php143
-rw-r--r--server/vendor/guzzlehttp/promises/tests/RejectionExceptionTest.php47
-rw-r--r--server/vendor/guzzlehttp/promises/tests/TaskQueueTest.php31
-rw-r--r--server/vendor/guzzlehttp/promises/tests/Thennable.php24
-rw-r--r--server/vendor/guzzlehttp/promises/tests/bootstrap.php4
-rw-r--r--server/vendor/guzzlehttp/promises/tests/functionsTest.php694
31 files changed, 4019 insertions, 0 deletions
diff --git a/server/vendor/guzzlehttp/promises/.gitignore b/server/vendor/guzzlehttp/promises/.gitignore
new file mode 100644
index 0000000..83ec41e
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/.gitignore
@@ -0,0 +1,11 @@
+phpunit.xml
+composer.phar
+composer.lock
+composer-test.lock
+vendor/
+build/artifacts/
+artifacts/
+docs/_build
+docs/*.pyc
+.idea
+.DS_STORE
diff --git a/server/vendor/guzzlehttp/promises/.travis.yml b/server/vendor/guzzlehttp/promises/.travis.yml
new file mode 100644
index 0000000..4f4d2b8
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/.travis.yml
@@ -0,0 +1,19 @@
+language: php
+
+php:
+ - 5.5
+ - 5.6
+ - 7.0
+ - hhvm
+
+sudo: false
+
+install:
+ - travis_retry composer install --no-interaction --prefer-source
+
+script: make test
+
+matrix:
+ allow_failures:
+ - php: hhvm
+ fast_finish: true
diff --git a/server/vendor/guzzlehttp/promises/CHANGELOG.md b/server/vendor/guzzlehttp/promises/CHANGELOG.md
new file mode 100644
index 0000000..4031cb8
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/CHANGELOG.md
@@ -0,0 +1,21 @@
+# CHANGELOG
+
+## 1.0.3 - 2015-10-15
+
+* Update EachPromise to immediately resolve when the underlying promise iterator
+ is empty. Previously, such a promise would throw an exception when its `wait`
+ function was called.
+
+## 1.0.2 - 2015-05-15
+
+* Conditionally require functions.php.
+
+## 1.0.1 - 2015-06-24
+
+* Updating EachPromise to call next on the underlying promise iterator as late
+ as possible to ensure that generators that generate new requests based on
+ callbacks are not iterated until after callbacks are invoked.
+
+## 1.0.0 - 2015-05-12
+
+* Initial release
diff --git a/server/vendor/guzzlehttp/promises/LICENSE b/server/vendor/guzzlehttp/promises/LICENSE
new file mode 100644
index 0000000..581d95f
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
+
+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.
diff --git a/server/vendor/guzzlehttp/promises/Makefile b/server/vendor/guzzlehttp/promises/Makefile
new file mode 100644
index 0000000..8d5b3ef
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/Makefile
@@ -0,0 +1,13 @@
+all: clean test
+
+test:
+ vendor/bin/phpunit
+
+coverage:
+ vendor/bin/phpunit --coverage-html=artifacts/coverage
+
+view-coverage:
+ open artifacts/coverage/index.html
+
+clean:
+ rm -rf artifacts/*
diff --git a/server/vendor/guzzlehttp/promises/README.md b/server/vendor/guzzlehttp/promises/README.md
new file mode 100644
index 0000000..c6780ab
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/README.md
@@ -0,0 +1,501 @@
+# Guzzle Promises
+
+[Promises/A+](https://promisesaplus.com/) implementation that handles promise
+chaining and resolution iteratively, allowing for "infinite" promise chaining
+while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
+for a general introduction to promises.
+
+- [Features](#features)
+- [Quick start](#quick-start)
+- [Synchronous wait](#synchronous-wait)
+- [Cancellation](#cancellation)
+- [API](#api)
+ - [Promise](#promise)
+ - [FulfilledPromise](#fulfilledpromise)
+ - [RejectedPromise](#rejectedpromise)
+- [Promise interop](#promise-interop)
+- [Implementation notes](#implementation-notes)
+
+
+# Features
+
+- [Promises/A+](https://promisesaplus.com/) implementation.
+- Promise resolution and chaining is handled iteratively, allowing for
+ "infinite" promise chaining.
+- Promises have a synchronous `wait` method.
+- Promises can be cancelled.
+- Works with any object that has a `then` function.
+- C# style async/await coroutine promises using
+ `GuzzleHttp\Promise\coroutine()`.
+
+
+# Quick start
+
+A *promise* represents the eventual result of an asynchronous operation. The
+primary way of interacting with a promise is through its `then` method, which
+registers callbacks to receive either a promise's eventual value or the reason
+why the promise cannot be fulfilled.
+
+
+## Callbacks
+
+Callbacks are registered with the `then` method by providing an optional
+`$onFulfilled` followed by an optional `$onRejected` function.
+
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(
+ // $onFulfilled
+ function ($value) {
+ echo 'The promise was fulfilled.';
+ },
+ // $onRejected
+ function ($reason) {
+ echo 'The promise was rejected.';
+ }
+);
+```
+
+*Resolving* a promise means that you either fulfill a promise with a *value* or
+reject a promise with a *reason*. Resolving a promises triggers callbacks
+registered with the promises's `then` method. These callbacks are triggered
+only once and in the order in which they were added.
+
+
+## Resolving a promise
+
+Promises are fulfilled using the `resolve($value)` method. Resolving a promise
+with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
+all of the onFulfilled callbacks (resolving a promise with a rejected promise
+will reject the promise and trigger the `$onRejected` callbacks).
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise
+ ->then(function ($value) {
+ // Return a value and don't break the chain
+ return "Hello, " . $value;
+ })
+ // This then is executed after the first then and receives the value
+ // returned from the first then.
+ ->then(function ($value) {
+ echo $value;
+ });
+
+// Resolving the promise triggers the $onFulfilled callbacks and outputs
+// "Hello, reader".
+$promise->resolve('reader.');
+```
+
+
+## Promise forwarding
+
+Promises can be chained one after the other. Each then in the chain is a new
+promise. The return value of of a promise is what's forwarded to the next
+promise in the chain. Returning a promise in a `then` callback will cause the
+subsequent promises in the chain to only be fulfilled when the returned promise
+has been fulfilled. The next promise in the chain will be invoked with the
+resolved value of the promise.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$nextPromise = new Promise();
+
+$promise
+ ->then(function ($value) use ($nextPromise) {
+ echo $value;
+ return $nextPromise;
+ })
+ ->then(function ($value) {
+ echo $value;
+ });
+
+// Triggers the first callback and outputs "A"
+$promise->resolve('A');
+// Triggers the second callback and outputs "B"
+$nextPromise->resolve('B');
+```
+
+## Promise rejection
+
+When a promise is rejected, the `$onRejected` callbacks are invoked with the
+rejection reason.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+ echo $reason;
+});
+
+$promise->reject('Error!');
+// Outputs "Error!"
+```
+
+## Rejection forwarding
+
+If an exception is thrown in an `$onRejected` callback, subsequent
+`$onRejected` callbacks are invoked with the thrown exception as the reason.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+ throw new \Exception($reason);
+})->then(null, function ($reason) {
+ assert($reason->getMessage() === 'Error!');
+});
+
+$promise->reject('Error!');
+```
+
+You can also forward a rejection down the promise chain by returning a
+`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
+`$onRejected` callback.
+
+```php
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+ return new RejectedPromise($reason);
+})->then(null, function ($reason) {
+ assert($reason === 'Error!');
+});
+
+$promise->reject('Error!');
+```
+
+If an exception is not thrown in a `$onRejected` callback and the callback
+does not return a rejected promise, downstream `$onFulfilled` callbacks are
+invoked using the value returned from the `$onRejected` callback.
+
+```php
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new Promise();
+$promise
+ ->then(null, function ($reason) {
+ return "It's ok";
+ })
+ ->then(function ($value) {
+ assert($value === "It's ok");
+ });
+
+$promise->reject('Error!');
+```
+
+# Synchronous wait
+
+You can synchronously force promises to complete using a promise's `wait`
+method. When creating a promise, you can provide a wait function that is used
+to synchronously force a promise to complete. When a wait function is invoked
+it is expected to deliver a value to the promise or reject the promise. If the
+wait function does not deliver a value, then an exception is thrown. The wait
+function provided to a promise constructor is invoked when the `wait` function
+of the promise is called.
+
+```php
+$promise = new Promise(function () use (&$promise) {
+ $promise->deliver('foo');
+});
+
+// Calling wait will return the value of the promise.
+echo $promise->wait(); // outputs "foo"
+```
+
+If an exception is encountered while invoking the wait function of a promise,
+the promise is rejected with the exception and the exception is thrown.
+
+```php
+$promise = new Promise(function () use (&$promise) {
+ throw new \Exception('foo');
+});
+
+$promise->wait(); // throws the exception.
+```
+
+Calling `wait` on a promise that has been fulfilled will not trigger the wait
+function. It will simply return the previously delivered value.
+
+```php
+$promise = new Promise(function () { die('this is not called!'); });
+$promise->deliver('foo');
+echo $promise->wait(); // outputs "foo"
+```
+
+Calling `wait` on a promise that has been rejected will throw an exception. If
+the rejection reason is an instance of `\Exception` the reason is thrown.
+Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
+can be obtained by calling the `getReason` method of the exception.
+
+```php
+$promise = new Promise();
+$promise->reject('foo');
+$promise->wait();
+```
+
+> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
+
+
+## Unwrapping a promise
+
+When synchronously waiting on a promise, you are joining the state of the
+promise into the current state of execution (i.e., return the value of the
+promise if it was fulfilled or throw an exception if it was rejected). This is
+called "unwrapping" the promise. Waiting on a promise will by default unwrap
+the promise state.
+
+You can force a promise to resolve and *not* unwrap the state of the promise
+by passing `false` to the first argument of the `wait` function:
+
+```php
+$promise = new Promise();
+$promise->reject('foo');
+// This will not throw an exception. It simply ensures the promise has
+// been resolved.
+$promise->wait(false);
+```
+
+When unwrapping a promise, the delivered value of the promise will be waited
+upon until the unwrapped value is not a promise. This means that if you resolve
+promise A with a promise B and unwrap promise A, the value returned by the
+wait function will be the value delivered to promise B.
+
+**Note**: when you do not unwrap the promise, no value is returned.
+
+
+# Cancellation
+
+You can cancel a promise that has not yet been fulfilled using the `cancel()`
+method of a promise. When creating a promise you can provide an optional
+cancel function that when invoked cancels the action of computing a resolution
+of the promise.
+
+
+# API
+
+
+## Promise
+
+When creating a promise object, you can provide an optional `$waitFn` and
+`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
+expected to resolve the promise. `$cancelFn` is a function with no arguments
+that is expected to cancel the computation of a promise. It is invoked when the
+`cancel()` method of a promise is called.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise(
+ function () use (&$promise) {
+ $promise->resolve('waited');
+ },
+ function () {
+ // do something that will cancel the promise computation (e.g., close
+ // a socket, cancel a database query, etc...)
+ }
+);
+
+assert('waited' === $promise->wait());
+```
+
+A promise has the following methods:
+
+- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
+
+ Creates a new promise that is fulfilled or rejected when the promise is
+ resolved.
+
+- `wait($unwrap = true) : mixed`
+
+ Synchronously waits on the promise to complete.
+
+ `$unwrap` controls whether or not the value of the promise is returned for a
+ fulfilled promise or if an exception is thrown if the promise is rejected.
+ This is set to `true` by default.
+
+- `cancel()`
+
+ Attempts to cancel the promise if possible. The promise being cancelled and
+ the parent most ancestor that has not yet been resolved will also be
+ cancelled. Any promises waiting on the cancelled promise to resolve will also
+ be cancelled.
+
+- `getState() : string`
+
+ Returns the state of the promise. One of `pending`, `fulfilled`, or
+ `rejected`.
+
+- `resolve($value)`
+
+ Fulfills the promise with the given `$value`.
+
+- `reject($reason)`
+
+ Rejects the promise with the given `$reason`.
+
+
+## FulfilledPromise
+
+A fulfilled promise can be created to represent a promise that has been
+fulfilled.
+
+```php
+use GuzzleHttp\Promise\FulfilledPromise;
+
+$promise = new FulfilledPromise('value');
+
+// Fulfilled callbacks are immediately invoked.
+$promise->then(function ($value) {
+ echo $value;
+});
+```
+
+
+## RejectedPromise
+
+A rejected promise can be created to represent a promise that has been
+rejected.
+
+```php
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new RejectedPromise('Error');
+
+// Rejected callbacks are immediately invoked.
+$promise->then(null, function ($reason) {
+ echo $reason;
+});
+```
+
+
+# Promise interop
+
+This library works with foreign promises that have a `then` method. This means
+you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
+for example. When a foreign promise is returned inside of a then method
+callback, promise resolution will occur recursively.
+
+```php
+// Create a React promise
+$deferred = new React\Promise\Deferred();
+$reactPromise = $deferred->promise();
+
+// Create a Guzzle promise that is fulfilled with a React promise.
+$guzzlePromise = new \GuzzleHttp\Promise\Promise();
+$guzzlePromise->then(function ($value) use ($reactPromise) {
+ // Do something something with the value...
+ // Return the React promise
+ return $reactPromise;
+});
+```
+
+Please note that wait and cancel chaining is no longer possible when forwarding
+a foreign promise. You will need to wrap a third-party promise with a Guzzle
+promise in order to utilize wait and cancel functions with foreign promises.
+
+
+## Event Loop Integration
+
+In order to keep the stack size constant, Guzzle promises are resolved
+asynchronously using a task queue. When waiting on promises synchronously, the
+task queue will be automatically run to ensure that the blocking promise and
+any forwarded promises are resolved. When using promises asynchronously in an
+event loop, you will need to run the task queue on each tick of the loop. If
+you do not run the task queue, then promises will not be resolved.
+
+You can run the task queue using the `run()` method of the global task queue
+instance.
+
+```php
+// Get the global task queue
+$queue = \GuzzleHttp\Promise\queue();
+$queue->run();
+```
+
+For example, you could use Guzzle promises with React using a periodic timer:
+
+```php
+$loop = React\EventLoop\Factory::create();
+$loop->addPeriodicTimer(0, [$queue, 'run']);
+```
+
+*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
+
+
+# Implementation notes
+
+
+## Promise resolution and chaining is handled iteratively
+
+By shuffling pending handlers from one owner to another, promises are
+resolved iteratively, allowing for "infinite" then chaining.
+
+```php
+<?php
+require 'vendor/autoload.php';
+
+use GuzzleHttp\Promise\Promise;
+
+$parent = new Promise();
+$p = $parent;
+
+for ($i = 0; $i < 1000; $i++) {
+ $p = $p->then(function ($v) {
+ // The stack size remains constant (a good thing)
+ echo xdebug_get_stack_depth() . ', ';
+ return $v + 1;
+ });
+}
+
+$parent->resolve(0);
+var_dump($p->wait()); // int(1000)
+
+```
+
+When a promise is fulfilled or rejected with a non-promise value, the promise
+then takes ownership of the handlers of each child promise and delivers values
+down the chain without using recursion.
+
+When a promise is resolved with another promise, the original promise transfers
+all of its pending handlers to the new promise. When the new promise is
+eventually resolved, all of the pending handlers are delivered the forwarded
+value.
+
+
+## A promise is the deferred.
+
+Some promise libraries implement promises using a deferred object to represent
+a computation and a promise object to represent the delivery of the result of
+the computation. This is a nice separation of computation and delivery because
+consumers of the promise cannot modify the value that will be eventually
+delivered.
+
+One side effect of being able to implement promise resolution and chaining
+iteratively is that you need to be able for one promise to reach into the state
+of another promise to shuffle around ownership of handlers. In order to achieve
+this without making the handlers of a promise publicly mutable, a promise is
+also the deferred value, allowing promises of the same parent class to reach
+into and modify the private properties of promises of the same type. While this
+does allow consumers of the value to modify the resolution or rejection of the
+deferred, it is a small price to pay for keeping the stack size constant.
+
+```php
+$promise = new Promise();
+$promise->then(function ($value) { echo $value; });
+// The promise is the deferred value, so you can deliver a value to it.
+$promise->deliver('foo');
+// prints "foo"
+```
diff --git a/server/vendor/guzzlehttp/promises/composer.json b/server/vendor/guzzlehttp/promises/composer.json
new file mode 100644
index 0000000..f13844b
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/composer.json
@@ -0,0 +1,31 @@
+{
+ "name": "guzzlehttp/promises",
+ "type": "library",
+ "description": "Guzzle promises library",
+ "keywords": ["promise"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ },
+ "files": ["src/functions_include.php"]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/phpunit.xml.dist b/server/vendor/guzzlehttp/promises/phpunit.xml.dist
new file mode 100644
index 0000000..500cd53
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/phpunit.xml.dist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit bootstrap="./tests/bootstrap.php"
+ colors="true">
+ <testsuites>
+ <testsuite>
+ <directory>tests</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist>
+ <directory suffix=".php">src</directory>
+ <exclude>
+ <directory suffix="Interface.php">src/</directory>
+ </exclude>
+ </whitelist>
+ </filter>
+</phpunit>
diff --git a/server/vendor/guzzlehttp/promises/src/AggregateException.php b/server/vendor/guzzlehttp/promises/src/AggregateException.php
new file mode 100644
index 0000000..6a5690c
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/AggregateException.php
@@ -0,0 +1,16 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Exception thrown when too many errors occur in the some() or any() methods.
+ */
+class AggregateException extends RejectionException
+{
+ public function __construct($msg, array $reasons)
+ {
+ parent::__construct(
+ $reasons,
+ sprintf('%s; %d rejected promises', $msg, count($reasons))
+ );
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/src/CancellationException.php b/server/vendor/guzzlehttp/promises/src/CancellationException.php
new file mode 100644
index 0000000..cb360b8
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/CancellationException.php
@@ -0,0 +1,9 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Exception that is set as the reason for a promise that has been cancelled.
+ */
+class CancellationException extends RejectionException
+{
+}
diff --git a/server/vendor/guzzlehttp/promises/src/EachPromise.php b/server/vendor/guzzlehttp/promises/src/EachPromise.php
new file mode 100644
index 0000000..5918429
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/EachPromise.php
@@ -0,0 +1,207 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Represents a promise that iterates over many promises and invokes
+ * side-effect functions in the process.
+ */
+class EachPromise implements PromisorInterface
+{
+ private $pending = [];
+
+ /** @var \Iterator */
+ private $iterable;
+
+ /** @var callable|int */
+ private $concurrency;
+
+ /** @var callable */
+ private $onFulfilled;
+
+ /** @var callable */
+ private $onRejected;
+
+ /** @var Promise */
+ private $aggregate;
+
+ /**
+ * Configuration hash can include the following key value pairs:
+ *
+ * - fulfilled: (callable) Invoked when a promise fulfills. The function
+ * is invoked with three arguments: the fulfillment value, the index
+ * position from the iterable list of the promise, and the aggregate
+ * promise that manages all of the promises. The aggregate promise may
+ * be resolved from within the callback to short-circuit the promise.
+ * - rejected: (callable) Invoked when a promise is rejected. The
+ * function is invoked with three arguments: the rejection reason, the
+ * index position from the iterable list of the promise, and the
+ * aggregate promise that manages all of the promises. The aggregate
+ * promise may be resolved from within the callback to short-circuit
+ * the promise.
+ * - concurrency: (integer) Pass this configuration option to limit the
+ * allowed number of outstanding concurrently executing promises,
+ * creating a capped pool of promises. There is no limit by default.
+ *
+ * @param mixed $iterable Promises or values to iterate.
+ * @param array $config Configuration options
+ */
+ public function __construct($iterable, array $config = [])
+ {
+ $this->iterable = iter_for($iterable);
+
+ if (isset($config['concurrency'])) {
+ $this->concurrency = $config['concurrency'];
+ }
+
+ if (isset($config['fulfilled'])) {
+ $this->onFulfilled = $config['fulfilled'];
+ }
+
+ if (isset($config['rejected'])) {
+ $this->onRejected = $config['rejected'];
+ }
+ }
+
+ public function promise()
+ {
+ if ($this->aggregate) {
+ return $this->aggregate;
+ }
+
+ try {
+ $this->createPromise();
+ $this->iterable->rewind();
+ $this->refillPending();
+ } catch (\Exception $e) {
+ $this->aggregate->reject($e);
+ }
+
+ return $this->aggregate;
+ }
+
+ private function createPromise()
+ {
+ $this->aggregate = new Promise(function () {
+ reset($this->pending);
+ if (empty($this->pending) && !$this->iterable->valid()) {
+ $this->aggregate->resolve(null);
+ return;
+ }
+
+ // Consume a potentially fluctuating list of promises while
+ // ensuring that indexes are maintained (precluding array_shift).
+ while ($promise = current($this->pending)) {
+ next($this->pending);
+ $promise->wait();
+ if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
+ return;
+ }
+ }
+ });
+
+ // Clear the references when the promise is resolved.
+ $clearFn = function () {
+ $this->iterable = $this->concurrency = $this->pending = null;
+ $this->onFulfilled = $this->onRejected = null;
+ };
+
+ $this->aggregate->then($clearFn, $clearFn);
+ }
+
+ private function refillPending()
+ {
+ if (!$this->concurrency) {
+ // Add all pending promises.
+ while ($this->addPending() && $this->advanceIterator());
+ return;
+ }
+
+ // Add only up to N pending promises.
+ $concurrency = is_callable($this->concurrency)
+ ? call_user_func($this->concurrency, count($this->pending))
+ : $this->concurrency;
+ $concurrency = max($concurrency - count($this->pending), 0);
+ // Concurrency may be set to 0 to disallow new promises.
+ if (!$concurrency) {
+ return;
+ }
+ // Add the first pending promise.
+ $this->addPending();
+ // Note this is special handling for concurrency=1 so that we do
+ // not advance the iterator after adding the first promise. This
+ // helps work around issues with generators that might not have the
+ // next value to yield until promise callbacks are called.
+ while (--$concurrency
+ && $this->advanceIterator()
+ && $this->addPending());
+ }
+
+ private function addPending()
+ {
+ if (!$this->iterable || !$this->iterable->valid()) {
+ return false;
+ }
+
+ $promise = promise_for($this->iterable->current());
+ $idx = $this->iterable->key();
+
+ $this->pending[$idx] = $promise->then(
+ function ($value) use ($idx) {
+ if ($this->onFulfilled) {
+ call_user_func(
+ $this->onFulfilled, $value, $idx, $this->aggregate
+ );
+ }
+ $this->step($idx);
+ },
+ function ($reason) use ($idx) {
+ if ($this->onRejected) {
+ call_user_func(
+ $this->onRejected, $reason, $idx, $this->aggregate
+ );
+ }
+ $this->step($idx);
+ }
+ );
+
+ return true;
+ }
+
+ private function advanceIterator()
+ {
+ try {
+ $this->iterable->next();
+ return true;
+ } catch (\Exception $e) {
+ $this->aggregate->reject($e);
+ return false;
+ }
+ }
+
+ private function step($idx)
+ {
+ // If the promise was already resolved, then ignore this step.
+ if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
+ return;
+ }
+
+ unset($this->pending[$idx]);
+ $this->advanceIterator();
+
+ if (!$this->checkIfFinished()) {
+ // Add more pending promises if possible.
+ $this->refillPending();
+ }
+ }
+
+ private function checkIfFinished()
+ {
+ if (!$this->pending && !$this->iterable->valid()) {
+ // Resolve the promise if there's nothing left to do.
+ $this->aggregate->resolve(null);
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/src/FulfilledPromise.php b/server/vendor/guzzlehttp/promises/src/FulfilledPromise.php
new file mode 100644
index 0000000..5596296
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/FulfilledPromise.php
@@ -0,0 +1,80 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise that has been fulfilled.
+ *
+ * Thenning off of this promise will invoke the onFulfilled callback
+ * immediately and ignore other callbacks.
+ */
+class FulfilledPromise implements PromiseInterface
+{
+ private $value;
+
+ public function __construct($value)
+ {
+ if (method_exists($value, 'then')) {
+ throw new \InvalidArgumentException(
+ 'You cannot create a FulfilledPromise with a promise.');
+ }
+
+ $this->value = $value;
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ) {
+ // Return itself if there is no onFulfilled function.
+ if (!$onFulfilled) {
+ return $this;
+ }
+
+ $queue = queue();
+ $p = new Promise([$queue, 'run']);
+ $value = $this->value;
+ $queue->add(static function () use ($p, $value, $onFulfilled) {
+ if ($p->getState() === self::PENDING) {
+ try {
+ $p->resolve($onFulfilled($value));
+ } catch (\Exception $e) {
+ $p->reject($e);
+ }
+ }
+ });
+
+ return $p;
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this->then(null, $onRejected);
+ }
+
+ public function wait($unwrap = true, $defaultDelivery = null)
+ {
+ return $unwrap ? $this->value : null;
+ }
+
+ public function getState()
+ {
+ return self::FULFILLED;
+ }
+
+ public function resolve($value)
+ {
+ if ($value !== $this->value) {
+ throw new \LogicException("Cannot resolve a fulfilled promise");
+ }
+ }
+
+ public function reject($reason)
+ {
+ throw new \LogicException("Cannot reject a fulfilled promise");
+ }
+
+ public function cancel()
+ {
+ // pass
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/src/Promise.php b/server/vendor/guzzlehttp/promises/src/Promise.php
new file mode 100644
index 0000000..c2cf969
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/Promise.php
@@ -0,0 +1,268 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Promises/A+ implementation that avoids recursion when possible.
+ *
+ * @link https://promisesaplus.com/
+ */
+class Promise implements PromiseInterface
+{
+ private $state = self::PENDING;
+ private $result;
+ private $cancelFn;
+ private $waitFn;
+ private $waitList;
+ private $handlers = [];
+
+ /**
+ * @param callable $waitFn Fn that when invoked resolves the promise.
+ * @param callable $cancelFn Fn that when invoked cancels the promise.
+ */
+ public function __construct(
+ callable $waitFn = null,
+ callable $cancelFn = null
+ ) {
+ $this->waitFn = $waitFn;
+ $this->cancelFn = $cancelFn;
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ) {
+ if ($this->state === self::PENDING) {
+ $p = new Promise(null, [$this, 'cancel']);
+ $this->handlers[] = [$p, $onFulfilled, $onRejected];
+ $p->waitList = $this->waitList;
+ $p->waitList[] = $this;
+ return $p;
+ }
+
+ // Return a fulfilled promise and immediately invoke any callbacks.
+ if ($this->state === self::FULFILLED) {
+ return $onFulfilled
+ ? promise_for($this->result)->then($onFulfilled)
+ : promise_for($this->result);
+ }
+
+ // It's either cancelled or rejected, so return a rejected promise
+ // and immediately invoke any callbacks.
+ $rejection = rejection_for($this->result);
+ return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this->then(null, $onRejected);
+ }
+
+ public function wait($unwrap = true)
+ {
+ $this->waitIfPending();
+
+ if (!$unwrap) {
+ return null;
+ }
+
+ if ($this->result instanceof PromiseInterface) {
+ return $this->result->wait($unwrap);
+ } elseif ($this->state === self::FULFILLED) {
+ return $this->result;
+ } else {
+ // It's rejected so "unwrap" and throw an exception.
+ throw exception_for($this->result);
+ }
+ }
+
+ public function getState()
+ {
+ return $this->state;
+ }
+
+ public function cancel()
+ {
+ if ($this->state !== self::PENDING) {
+ return;
+ }
+
+ $this->waitFn = $this->waitList = null;
+
+ if ($this->cancelFn) {
+ $fn = $this->cancelFn;
+ $this->cancelFn = null;
+ try {
+ $fn();
+ } catch (\Exception $e) {
+ $this->reject($e);
+ }
+ }
+
+ // Reject the promise only if it wasn't rejected in a then callback.
+ if ($this->state === self::PENDING) {
+ $this->reject(new CancellationException('Promise has been cancelled'));
+ }
+ }
+
+ public function resolve($value)
+ {
+ $this->settle(self::FULFILLED, $value);
+ }
+
+ public function reject($reason)
+ {
+ $this->settle(self::REJECTED, $reason);
+ }
+
+ private function settle($state, $value)
+ {
+ if ($this->state !== self::PENDING) {
+ // Ignore calls with the same resolution.
+ if ($state === $this->state && $value === $this->result) {
+ return;
+ }
+ throw $this->state === $state
+ ? new \LogicException("The promise is already {$state}.")
+ : new \LogicException("Cannot change a {$this->state} promise to {$state}");
+ }
+
+ if ($value === $this) {
+ throw new \LogicException('Cannot fulfill or reject a promise with itself');
+ }
+
+ // Clear out the state of the promise but stash the handlers.
+ $this->state = $state;
+ $this->result = $value;
+ $handlers = $this->handlers;
+ $this->handlers = null;
+ $this->waitList = $this->waitFn = null;
+ $this->cancelFn = null;
+
+ if (!$handlers) {
+ return;
+ }
+
+ // If the value was not a settled promise or a thenable, then resolve
+ // it in the task queue using the correct ID.
+ if (!method_exists($value, 'then')) {
+ $id = $state === self::FULFILLED ? 1 : 2;
+ // It's a success, so resolve the handlers in the queue.
+ queue()->add(static function () use ($id, $value, $handlers) {
+ foreach ($handlers as $handler) {
+ self::callHandler($id, $value, $handler);
+ }
+ });
+ } elseif ($value instanceof Promise
+ && $value->getState() === self::PENDING
+ ) {
+ // We can just merge our handlers onto the next promise.
+ $value->handlers = array_merge($value->handlers, $handlers);
+ } else {
+ // Resolve the handlers when the forwarded promise is resolved.
+ $value->then(
+ static function ($value) use ($handlers) {
+ foreach ($handlers as $handler) {
+ self::callHandler(1, $value, $handler);
+ }
+ },
+ static function ($reason) use ($handlers) {
+ foreach ($handlers as $handler) {
+ self::callHandler(2, $reason, $handler);
+ }
+ }
+ );
+ }
+ }
+
+ /**
+ * Call a stack of handlers using a specific callback index and value.
+ *
+ * @param int $index 1 (resolve) or 2 (reject).
+ * @param mixed $value Value to pass to the callback.
+ * @param array $handler Array of handler data (promise and callbacks).
+ *
+ * @return array Returns the next group to resolve.
+ */
+ private static function callHandler($index, $value, array $handler)
+ {
+ /** @var PromiseInterface $promise */
+ $promise = $handler[0];
+
+ // The promise may have been cancelled or resolved before placing
+ // this thunk in the queue.
+ if ($promise->getState() !== self::PENDING) {
+ return;
+ }
+
+ try {
+ if (isset($handler[$index])) {
+ $promise->resolve($handler[$index]($value));
+ } elseif ($index === 1) {
+ // Forward resolution values as-is.
+ $promise->resolve($value);
+ } else {
+ // Forward rejections down the chain.
+ $promise->reject($value);
+ }
+ } catch (\Exception $reason) {
+ $promise->reject($reason);
+ }
+ }
+
+ private function waitIfPending()
+ {
+ if ($this->state !== self::PENDING) {
+ return;
+ } elseif ($this->waitFn) {
+ $this->invokeWaitFn();
+ } elseif ($this->waitList) {
+ $this->invokeWaitList();
+ } else {
+ // If there's not wait function, then reject the promise.
+ $this->reject('Cannot wait on a promise that has '
+ . 'no internal wait function. You must provide a wait '
+ . 'function when constructing the promise to be able to '
+ . 'wait on a promise.');
+ }
+
+ queue()->run();
+
+ if ($this->state === self::PENDING) {
+ $this->reject('Invoking the wait callback did not resolve the promise');
+ }
+ }
+
+ private function invokeWaitFn()
+ {
+ try {
+ $wfn = $this->waitFn;
+ $this->waitFn = null;
+ $wfn(true);
+ } catch (\Exception $reason) {
+ if ($this->state === self::PENDING) {
+ // The promise has not been resolved yet, so reject the promise
+ // with the exception.
+ $this->reject($reason);
+ } else {
+ // The promise was already resolved, so there's a problem in
+ // the application.
+ throw $reason;
+ }
+ }
+ }
+
+ private function invokeWaitList()
+ {
+ $waitList = $this->waitList;
+ $this->waitList = null;
+
+ foreach ($waitList as $result) {
+ descend:
+ $result->waitIfPending();
+ if ($result->result instanceof Promise) {
+ $result = $result->result;
+ goto descend;
+ }
+ }
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/src/PromiseInterface.php b/server/vendor/guzzlehttp/promises/src/PromiseInterface.php
new file mode 100644
index 0000000..8f5f4b9
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/PromiseInterface.php
@@ -0,0 +1,93 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise represents the eventual result of an asynchronous operation.
+ *
+ * The primary way of interacting with a promise is through its then method,
+ * which registers callbacks to receive either a promise’s eventual value or
+ * the reason why the promise cannot be fulfilled.
+ *
+ * @link https://promisesaplus.com/
+ */
+interface PromiseInterface
+{
+ const PENDING = 'pending';
+ const FULFILLED = 'fulfilled';
+ const REJECTED = 'rejected';
+
+ /**
+ * Appends fulfillment and rejection handlers to the promise, and returns
+ * a new promise resolving to the return value of the called handler.
+ *
+ * @param callable $onFulfilled Invoked when the promise fulfills.
+ * @param callable $onRejected Invoked when the promise is rejected.
+ *
+ * @return PromiseInterface
+ */
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ );
+
+ /**
+ * Appends a rejection handler callback to the promise, and returns a new
+ * promise resolving to the return value of the callback if it is called,
+ * or to its original fulfillment value if the promise is instead
+ * fulfilled.
+ *
+ * @param callable $onRejected Invoked when the promise is rejected.
+ *
+ * @return PromiseInterface
+ */
+ public function otherwise(callable $onRejected);
+
+ /**
+ * Get the state of the promise ("pending", "rejected", or "fulfilled").
+ *
+ * The three states can be checked against the constants defined on
+ * PromiseInterface: PENDING, FULFILLED, and REJECTED.
+ *
+ * @return string
+ */
+ public function getState();
+
+ /**
+ * Resolve the promise with the given value.
+ *
+ * @param mixed $value
+ * @throws \RuntimeException if the promise is already resolved.
+ */
+ public function resolve($value);
+
+ /**
+ * Reject the promise with the given reason.
+ *
+ * @param mixed $reason
+ * @throws \RuntimeException if the promise is already resolved.
+ */
+ public function reject($reason);
+
+ /**
+ * Cancels the promise if possible.
+ *
+ * @link https://github.com/promises-aplus/cancellation-spec/issues/7
+ */
+ public function cancel();
+
+ /**
+ * Waits until the promise completes if possible.
+ *
+ * Pass $unwrap as true to unwrap the result of the promise, either
+ * returning the resolved value or throwing the rejected exception.
+ *
+ * If the promise cannot be waited on, then the promise will be rejected.
+ *
+ * @param bool $unwrap
+ *
+ * @return mixed
+ * @throws \LogicException if the promise has no wait function or if the
+ * promise does not settle after waiting.
+ */
+ public function wait($unwrap = true);
+}
diff --git a/server/vendor/guzzlehttp/promises/src/PromisorInterface.php b/server/vendor/guzzlehttp/promises/src/PromisorInterface.php
new file mode 100644
index 0000000..b07fe32
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/PromisorInterface.php
@@ -0,0 +1,15 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Interface used with classes that return a promise.
+ */
+interface PromisorInterface
+{
+ /**
+ * Returns a promise.
+ *
+ * @return PromiseInterface
+ */
+ public function promise();
+}
diff --git a/server/vendor/guzzlehttp/promises/src/RejectedPromise.php b/server/vendor/guzzlehttp/promises/src/RejectedPromise.php
new file mode 100644
index 0000000..bd499e6
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/RejectedPromise.php
@@ -0,0 +1,84 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise that has been rejected.
+ *
+ * Thenning off of this promise will invoke the onRejected callback
+ * immediately and ignore other callbacks.
+ */
+class RejectedPromise implements PromiseInterface
+{
+ private $reason;
+
+ public function __construct($reason)
+ {
+ if (method_exists($reason, 'then')) {
+ throw new \InvalidArgumentException(
+ 'You cannot create a RejectedPromise with a promise.');
+ }
+
+ $this->reason = $reason;
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ) {
+ // If there's no onRejected callback then just return self.
+ if (!$onRejected) {
+ return $this;
+ }
+
+ $queue = queue();
+ $reason = $this->reason;
+ $p = new Promise([$queue, 'run']);
+ $queue->add(static function () use ($p, $reason, $onRejected) {
+ if ($p->getState() === self::PENDING) {
+ try {
+ // Return a resolved promise if onRejected does not throw.
+ $p->resolve($onRejected($reason));
+ } catch (\Exception $e) {
+ // onRejected threw, so return a rejected promise.
+ $p->reject($e);
+ }
+ }
+ });
+
+ return $p;
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this->then(null, $onRejected);
+ }
+
+ public function wait($unwrap = true, $defaultDelivery = null)
+ {
+ if ($unwrap) {
+ throw exception_for($this->reason);
+ }
+ }
+
+ public function getState()
+ {
+ return self::REJECTED;
+ }
+
+ public function resolve($value)
+ {
+ throw new \LogicException("Cannot resolve a rejected promise");
+ }
+
+ public function reject($reason)
+ {
+ if ($reason !== $this->reason) {
+ throw new \LogicException("Cannot reject a rejected promise");
+ }
+ }
+
+ public function cancel()
+ {
+ // pass
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/src/RejectionException.php b/server/vendor/guzzlehttp/promises/src/RejectionException.php
new file mode 100644
index 0000000..07c1136
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/RejectionException.php
@@ -0,0 +1,47 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A special exception that is thrown when waiting on a rejected promise.
+ *
+ * The reason value is available via the getReason() method.
+ */
+class RejectionException extends \RuntimeException
+{
+ /** @var mixed Rejection reason. */
+ private $reason;
+
+ /**
+ * @param mixed $reason Rejection reason.
+ * @param string $description Optional description
+ */
+ public function __construct($reason, $description = null)
+ {
+ $this->reason = $reason;
+
+ $message = 'The promise was rejected';
+
+ if ($description) {
+ $message .= ' with reason: ' . $description;
+ } elseif (is_string($reason)
+ || (is_object($reason) && method_exists($reason, '__toString'))
+ ) {
+ $message .= ' with reason: ' . $this->reason;
+ } elseif ($reason instanceof \JsonSerializable) {
+ $message .= ' with reason: '
+ . json_encode($this->reason, JSON_PRETTY_PRINT);
+ }
+
+ parent::__construct($message);
+ }
+
+ /**
+ * Returns the rejection reason.
+ *
+ * @return mixed
+ */
+ public function getReason()
+ {
+ return $this->reason;
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/src/TaskQueue.php b/server/vendor/guzzlehttp/promises/src/TaskQueue.php
new file mode 100644
index 0000000..5026363
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/TaskQueue.php
@@ -0,0 +1,79 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A task queue that executes tasks in a FIFO order.
+ *
+ * This task queue class is used to settle promises asynchronously and
+ * maintains a constant stack size. You can use the task queue asynchronously
+ * by calling the `run()` function of the global task queue in an event loop.
+ *
+ * GuzzleHttp\Promise\queue()->run();
+ */
+class TaskQueue
+{
+ private $enableShutdown = true;
+ private $queue = [];
+
+ public function __construct($withShutdown = true)
+ {
+ if ($withShutdown) {
+ register_shutdown_function(function () {
+ if ($this->enableShutdown) {
+ // Only run the tasks if an E_ERROR didn't occur.
+ $err = error_get_last();
+ if (!$err || ($err['type'] ^ E_ERROR)) {
+ $this->run();
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Returns true if the queue is empty.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return !$this->queue;
+ }
+
+ /**
+ * Adds a task to the queue that will be executed the next time run is
+ * called.
+ *
+ * @param callable $task
+ */
+ public function add(callable $task)
+ {
+ $this->queue[] = $task;
+ }
+
+ /**
+ * Execute all of the pending task in the queue.
+ */
+ public function run()
+ {
+ while ($task = array_shift($this->queue)) {
+ $task();
+ }
+ }
+
+ /**
+ * The task queue will be run and exhausted by default when the process
+ * exits IFF the exit is not the result of a PHP E_ERROR error.
+ *
+ * You can disable running the automatic shutdown of the queue by calling
+ * this function. If you disable the task queue shutdown process, then you
+ * MUST either run the task queue (as a result of running your event loop
+ * or manually using the run() method) or wait on each outstanding promise.
+ *
+ * Note: This shutdown will occur before any destructors are triggered.
+ */
+ public function disableShutdown()
+ {
+ $this->enableShutdown = false;
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/src/functions.php b/server/vendor/guzzlehttp/promises/src/functions.php
new file mode 100644
index 0000000..89c6569
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/functions.php
@@ -0,0 +1,495 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Get the global task queue used for promise resolution.
+ *
+ * This task queue MUST be run in an event loop in order for promises to be
+ * settled asynchronously. It will be automatically run when synchronously
+ * waiting on a promise.
+ *
+ * <code>
+ * while ($eventLoop->isRunning()) {
+ * GuzzleHttp\Promise\queue()->run();
+ * }
+ * </code>
+ *
+ * @return TaskQueue
+ */
+function queue()
+{
+ static $queue;
+
+ if (!$queue) {
+ $queue = new TaskQueue();
+ }
+
+ return $queue;
+}
+
+/**
+ * Adds a function to run in the task queue when it is next `run()` and returns
+ * a promise that is fulfilled or rejected with the result.
+ *
+ * @param callable $task Task function to run.
+ *
+ * @return PromiseInterface
+ */
+function task(callable $task)
+{
+ $queue = queue();
+ $promise = new Promise([$queue, 'run']);
+ $queue->add(function () use ($task, $promise) {
+ try {
+ $promise->resolve($task());
+ } catch (\Exception $e) {
+ $promise->reject($e);
+ }
+ });
+
+ return $promise;
+}
+
+/**
+ * Creates a promise for a value if the value is not a promise.
+ *
+ * @param mixed $value Promise or value.
+ *
+ * @return PromiseInterface
+ */
+function promise_for($value)
+{
+ if ($value instanceof PromiseInterface) {
+ return $value;
+ }
+
+ // Return a Guzzle promise that shadows the given promise.
+ if (method_exists($value, 'then')) {
+ $wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
+ $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
+ $promise = new Promise($wfn, $cfn);
+ $value->then([$promise, 'resolve'], [$promise, 'reject']);
+ return $promise;
+ }
+
+ return new FulfilledPromise($value);
+}
+
+/**
+ * Creates a rejected promise for a reason if the reason is not a promise. If
+ * the provided reason is a promise, then it is returned as-is.
+ *
+ * @param mixed $reason Promise or reason.
+ *
+ * @return PromiseInterface
+ */
+function rejection_for($reason)
+{
+ if ($reason instanceof PromiseInterface) {
+ return $reason;
+ }
+
+ return new RejectedPromise($reason);
+}
+
+/**
+ * Create an exception for a rejected promise value.
+ *
+ * @param mixed $reason
+ *
+ * @return \Exception
+ */
+function exception_for($reason)
+{
+ return $reason instanceof \Exception
+ ? $reason
+ : new RejectionException($reason);
+}
+
+/**
+ * Returns an iterator for the given value.
+ *
+ * @param mixed $value
+ *
+ * @return \Iterator
+ */
+function iter_for($value)
+{
+ if ($value instanceof \Iterator) {
+ return $value;
+ } elseif (is_array($value)) {
+ return new \ArrayIterator($value);
+ } else {
+ return new \ArrayIterator([$value]);
+ }
+}
+
+/**
+ * Synchronously waits on a promise to resolve and returns an inspection state
+ * array.
+ *
+ * Returns a state associative array containing a "state" key mapping to a
+ * valid promise state. If the state of the promise is "fulfilled", the array
+ * will contain a "value" key mapping to the fulfilled value of the promise. If
+ * the promise is rejected, the array will contain a "reason" key mapping to
+ * the rejection reason of the promise.
+ *
+ * @param PromiseInterface $promise Promise or value.
+ *
+ * @return array
+ */
+function inspect(PromiseInterface $promise)
+{
+ try {
+ return [
+ 'state' => PromiseInterface::FULFILLED,
+ 'value' => $promise->wait()
+ ];
+ } catch (RejectionException $e) {
+ return ['state' => 'rejected', 'reason' => $e->getReason()];
+ } catch (\Exception $e) {
+ return ['state' => 'rejected', 'reason' => $e];
+ }
+}
+
+/**
+ * Waits on all of the provided promises, but does not unwrap rejected promises
+ * as thrown exception.
+ *
+ * Returns an array of inspection state arrays.
+ *
+ * @param PromiseInterface[] $promises Traversable of promises to wait upon.
+ *
+ * @return array
+ * @see GuzzleHttp\Promise\inspect for the inspection state array format.
+ */
+function inspect_all($promises)
+{
+ $results = [];
+ foreach ($promises as $key => $promise) {
+ $results[$key] = inspect($promise);
+ }
+
+ return $results;
+}
+
+/**
+ * Waits on all of the provided promises and returns the fulfilled values.
+ *
+ * Returns an array that contains the value of each promise (in the same order
+ * the promises were provided). An exception is thrown if any of the promises
+ * are rejected.
+ *
+ * @param mixed $promises Iterable of PromiseInterface objects to wait on.
+ *
+ * @return array
+ * @throws \Exception on error
+ */
+function unwrap($promises)
+{
+ $results = [];
+ foreach ($promises as $key => $promise) {
+ $results[$key] = $promise->wait();
+ }
+
+ return $results;
+}
+
+/**
+ * Given an array of promises, return a promise that is fulfilled when all the
+ * items in the array are fulfilled.
+ *
+ * The promise's fulfillment value is an array with fulfillment values at
+ * respective positions to the original array. If any promise in the array
+ * rejects, the returned promise is rejected with the rejection reason.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return Promise
+ */
+function all($promises)
+{
+ $results = [];
+ return each(
+ $promises,
+ function ($value, $idx) use (&$results) {
+ $results[$idx] = $value;
+ },
+ function ($reason, $idx, Promise $aggregate) {
+ $aggregate->reject($reason);
+ }
+ )->then(function () use (&$results) {
+ ksort($results);
+ return $results;
+ });
+}
+
+/**
+ * Initiate a competitive race between multiple promises or values (values will
+ * become immediately fulfilled promises).
+ *
+ * When count amount of promises have been fulfilled, the returned promise is
+ * fulfilled with an array that contains the fulfillment values of the winners
+ * in order of resolution.
+ *
+ * This prommise is rejected with a {@see GuzzleHttp\Promise\AggregateException}
+ * if the number of fulfilled promises is less than the desired $count.
+ *
+ * @param int $count Total number of promises.
+ * @param mixed $promises Promises or values.
+ *
+ * @return Promise
+ */
+function some($count, $promises)
+{
+ $results = [];
+ $rejections = [];
+
+ return each(
+ $promises,
+ function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
+ if ($p->getState() !== PromiseInterface::PENDING) {
+ return;
+ }
+ $results[$idx] = $value;
+ if (count($results) >= $count) {
+ $p->resolve(null);
+ }
+ },
+ function ($reason) use (&$rejections) {
+ $rejections[] = $reason;
+ }
+ )->then(
+ function () use (&$results, &$rejections, $count) {
+ if (count($results) !== $count) {
+ throw new AggregateException(
+ 'Not enough promises to fulfill count',
+ $rejections
+ );
+ }
+ ksort($results);
+ return array_values($results);
+ }
+ );
+}
+
+/**
+ * Like some(), with 1 as count. However, if the promise fulfills, the
+ * fulfillment value is not an array of 1 but the value directly.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ */
+function any($promises)
+{
+ return some(1, $promises)->then(function ($values) { return $values[0]; });
+}
+
+/**
+ * Returns a promise that is fulfilled when all of the provided promises have
+ * been fulfilled or rejected.
+ *
+ * The returned promise is fulfilled with an array of inspection state arrays.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return Promise
+ * @see GuzzleHttp\Promise\inspect for the inspection state array format.
+ */
+function settle($promises)
+{
+ $results = [];
+
+ return each(
+ $promises,
+ function ($value, $idx) use (&$results) {
+ $results[$idx] = ['state' => 'fulfilled', 'value' => $value];
+ },
+ function ($reason, $idx) use (&$results) {
+ $results[$idx] = ['state' => 'rejected', 'reason' => $reason];
+ }
+ )->then(function () use (&$results) {
+ ksort($results);
+ return $results;
+ });
+}
+
+/**
+ * Given an iterator that yields promises or values, returns a promise that is
+ * fulfilled with a null value when the iterator has been consumed or the
+ * aggregate promise has been fulfilled or rejected.
+ *
+ * $onFulfilled is a function that accepts the fulfilled value, iterator
+ * index, and the aggregate promise. The callback can invoke any necessary side
+ * effects and choose to resolve or reject the aggregate promise if needed.
+ *
+ * $onRejected is a function that accepts the rejection reason, iterator
+ * index, and the aggregate promise. The callback can invoke any necessary side
+ * effects and choose to resolve or reject the aggregate promise if needed.
+ *
+ * @param mixed $iterable Iterator or array to iterate over.
+ * @param callable $onFulfilled
+ * @param callable $onRejected
+ *
+ * @return Promise
+ */
+function each(
+ $iterable,
+ callable $onFulfilled = null,
+ callable $onRejected = null
+) {
+ return (new EachPromise($iterable, [
+ 'fulfilled' => $onFulfilled,
+ 'rejected' => $onRejected
+ ]))->promise();
+}
+
+/**
+ * Like each, but only allows a certain number of outstanding promises at any
+ * given time.
+ *
+ * $concurrency may be an integer or a function that accepts the number of
+ * pending promises and returns a numeric concurrency limit value to allow for
+ * dynamic a concurrency size.
+ *
+ * @param mixed $iterable
+ * @param int|callable $concurrency
+ * @param callable $onFulfilled
+ * @param callable $onRejected
+ *
+ * @return mixed
+ */
+function each_limit(
+ $iterable,
+ $concurrency,
+ callable $onFulfilled = null,
+ callable $onRejected = null
+) {
+ return (new EachPromise($iterable, [
+ 'fulfilled' => $onFulfilled,
+ 'rejected' => $onRejected,
+ 'concurrency' => $concurrency
+ ]))->promise();
+}
+
+/**
+ * Like each_limit, but ensures that no promise in the given $iterable argument
+ * is rejected. If any promise is rejected, then the aggregate promise is
+ * rejected with the encountered rejection.
+ *
+ * @param mixed $iterable
+ * @param int|callable $concurrency
+ * @param callable $onFulfilled
+ *
+ * @return mixed
+ */
+function each_limit_all(
+ $iterable,
+ $concurrency,
+ callable $onFulfilled = null
+) {
+ return each_limit(
+ $iterable,
+ $concurrency,
+ $onFulfilled,
+ function ($reason, $idx, PromiseInterface $aggregate) {
+ $aggregate->reject($reason);
+ }
+ );
+}
+
+/**
+ * Returns true if a promise is fulfilled.
+ *
+ * @param PromiseInterface $promise
+ *
+ * @return bool
+ */
+function is_fulfilled(PromiseInterface $promise)
+{
+ return $promise->getState() === PromiseInterface::FULFILLED;
+}
+
+/**
+ * Returns true if a promise is rejected.
+ *
+ * @param PromiseInterface $promise
+ *
+ * @return bool
+ */
+function is_rejected(PromiseInterface $promise)
+{
+ return $promise->getState() === PromiseInterface::REJECTED;
+}
+
+/**
+ * Returns true if a promise is fulfilled or rejected.
+ *
+ * @param PromiseInterface $promise
+ *
+ * @return bool
+ */
+function is_settled(PromiseInterface $promise)
+{
+ return $promise->getState() !== PromiseInterface::PENDING;
+}
+
+/**
+ * Creates a promise that is resolved using a generator that yields values or
+ * promises (somewhat similar to C#'s async keyword).
+ *
+ * When called, the coroutine function will start an instance of the generator
+ * and returns a promise that is fulfilled with its final yielded value.
+ *
+ * Control is returned back to the generator when the yielded promise settles.
+ * This can lead to less verbose code when doing lots of sequential async calls
+ * with minimal processing in between.
+ *
+ * use GuzzleHttp\Promise;
+ *
+ * function createPromise($value) {
+ * return new Promise\FulfilledPromise($value);
+ * }
+ *
+ * $promise = Promise\coroutine(function () {
+ * $value = (yield createPromise('a'));
+ * try {
+ * $value = (yield createPromise($value . 'b'));
+ * } catch (\Exception $e) {
+ * // The promise was rejected.
+ * }
+ * yield $value . 'c';
+ * });
+ *
+ * // Outputs "abc"
+ * $promise->then(function ($v) { echo $v; });
+ *
+ * @param callable $generatorFn Generator function to wrap into a promise.
+ *
+ * @return Promise
+ * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
+ */
+function coroutine(callable $generatorFn)
+{
+ $generator = $generatorFn();
+ return __next_coroutine($generator->current(), $generator)->then();
+}
+
+/** @internal */
+function __next_coroutine($yielded, \Generator $generator)
+{
+ return promise_for($yielded)->then(
+ function ($value) use ($generator) {
+ $nextYield = $generator->send($value);
+ return $generator->valid()
+ ? __next_coroutine($nextYield, $generator)
+ : $value;
+ },
+ function ($reason) use ($generator) {
+ $nextYield = $generator->throw(exception_for($reason));
+ // The throw was caught, so keep iterating on the coroutine
+ return __next_coroutine($nextYield, $generator);
+ }
+ );
+}
diff --git a/server/vendor/guzzlehttp/promises/src/functions_include.php b/server/vendor/guzzlehttp/promises/src/functions_include.php
new file mode 100644
index 0000000..34cd171
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/src/functions_include.php
@@ -0,0 +1,6 @@
+<?php
+
+// Don't redefine the functions if included multiple times.
+if (!function_exists('GuzzleHttp\Promise\promise_for')) {
+ require __DIR__ . '/functions.php';
+}
diff --git a/server/vendor/guzzlehttp/promises/tests/AggregateExceptionTest.php b/server/vendor/guzzlehttp/promises/tests/AggregateExceptionTest.php
new file mode 100644
index 0000000..eaa7703
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/AggregateExceptionTest.php
@@ -0,0 +1,14 @@
+<?php
+namespace GuzzleHttp\Promise\Tests;
+
+use GuzzleHttp\Promise\AggregateException;
+
+class AggregateExceptionTest extends \PHPUnit_Framework_TestCase
+{
+ public function testHasReason()
+ {
+ $e = new AggregateException('foo', ['baz', 'bar']);
+ $this->assertContains('foo', $e->getMessage());
+ $this->assertEquals(['baz', 'bar'], $e->getReason());
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/tests/EachPromiseTest.php b/server/vendor/guzzlehttp/promises/tests/EachPromiseTest.php
new file mode 100644
index 0000000..0a0a851
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/EachPromiseTest.php
@@ -0,0 +1,294 @@
+<?php
+namespace GuzzleHttp\Promise\Tests;
+
+use GuzzleHttp\Promise\RejectedPromise;
+use GuzzleHttp\Promise\FulfilledPromise;
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Promise\EachPromise;
+use GuzzleHttp\Promise as P;
+
+/**
+ * @covers GuzzleHttp\Promise\EachPromise
+ */
+class EachPromiseTest extends \PHPUnit_Framework_TestCase
+{
+ public function testReturnsSameInstance()
+ {
+ $each = new EachPromise([], ['concurrency' => 100]);
+ $this->assertSame($each->promise(), $each->promise());
+ }
+
+ public function testInvokesAllPromises()
+ {
+ $promises = [new Promise(), new Promise(), new Promise()];
+ $called = [];
+ $each = new EachPromise($promises, [
+ 'fulfilled' => function ($value) use (&$called) {
+ $called[] = $value;
+ }
+ ]);
+ $p = $each->promise();
+ $promises[0]->resolve('a');
+ $promises[1]->resolve('c');
+ $promises[2]->resolve('b');
+ P\queue()->run();
+ $this->assertEquals(['a', 'c', 'b'], $called);
+ $this->assertEquals(PromiseInterface::FULFILLED, $p->getState());
+ }
+
+ public function testIsWaitable()
+ {
+ $a = new Promise(function () use (&$a) { $a->resolve('a'); });
+ $b = new Promise(function () use (&$b) { $b->resolve('b'); });
+ $called = [];
+ $each = new EachPromise([$a, $b], [
+ 'fulfilled' => function ($value) use (&$called) { $called[] = $value; }
+ ]);
+ $p = $each->promise();
+ $this->assertNull($p->wait());
+ $this->assertEquals(PromiseInterface::FULFILLED, $p->getState());
+ $this->assertEquals(['a', 'b'], $called);
+ }
+
+ public function testCanResolveBeforeConsumingAll()
+ {
+ $called = 0;
+ $a = new Promise(function () use (&$a) { $a->resolve('a'); });
+ $b = new Promise(function () { $this->fail(); });
+ $each = new EachPromise([$a, $b], [
+ 'fulfilled' => function ($value, $idx, Promise $aggregate) use (&$called) {
+ $this->assertSame($idx, 0);
+ $this->assertEquals('a', $value);
+ $aggregate->resolve(null);
+ $called++;
+ },
+ 'rejected' => function (\Exception $reason) {
+ $this->fail($reason->getMessage());
+ }
+ ]);
+ $p = $each->promise();
+ $p->wait();
+ $this->assertNull($p->wait());
+ $this->assertEquals(1, $called);
+ $this->assertEquals(PromiseInterface::FULFILLED, $a->getState());
+ $this->assertEquals(PromiseInterface::PENDING, $b->getState());
+ // Resolving $b has no effect on the aggregate promise.
+ $b->resolve('foo');
+ $this->assertEquals(1, $called);
+ }
+
+ public function testLimitsPendingPromises()
+ {
+ $pending = [new Promise(), new Promise(), new Promise(), new Promise()];
+ $promises = new \ArrayIterator($pending);
+ $each = new EachPromise($promises, ['concurrency' => 2]);
+ $p = $each->promise();
+ $this->assertCount(2, $this->readAttribute($each, 'pending'));
+ $pending[0]->resolve('a');
+ $this->assertCount(2, $this->readAttribute($each, 'pending'));
+ $this->assertTrue($promises->valid());
+ $pending[1]->resolve('b');
+ P\queue()->run();
+ $this->assertCount(2, $this->readAttribute($each, 'pending'));
+ $this->assertTrue($promises->valid());
+ $promises[2]->resolve('c');
+ P\queue()->run();
+ $this->assertCount(1, $this->readAttribute($each, 'pending'));
+ $this->assertEquals(PromiseInterface::PENDING, $p->getState());
+ $promises[3]->resolve('d');
+ P\queue()->run();
+ $this->assertNull($this->readAttribute($each, 'pending'));
+ $this->assertEquals(PromiseInterface::FULFILLED, $p->getState());
+ $this->assertFalse($promises->valid());
+ }
+
+ public function testDynamicallyLimitsPendingPromises()
+ {
+ $calls = [];
+ $pendingFn = function ($count) use (&$calls) {
+ $calls[] = $count;
+ return 2;
+ };
+ $pending = [new Promise(), new Promise(), new Promise(), new Promise()];
+ $promises = new \ArrayIterator($pending);
+ $each = new EachPromise($promises, ['concurrency' => $pendingFn]);
+ $p = $each->promise();
+ $this->assertCount(2, $this->readAttribute($each, 'pending'));
+ $pending[0]->resolve('a');
+ $this->assertCount(2, $this->readAttribute($each, 'pending'));
+ $this->assertTrue($promises->valid());
+ $pending[1]->resolve('b');
+ $this->assertCount(2, $this->readAttribute($each, 'pending'));
+ P\queue()->run();
+ $this->assertTrue($promises->valid());
+ $promises[2]->resolve('c');
+ P\queue()->run();
+ $this->assertCount(1, $this->readAttribute($each, 'pending'));
+ $this->assertEquals(PromiseInterface::PENDING, $p->getState());
+ $promises[3]->resolve('d');
+ P\queue()->run();
+ $this->assertNull($this->readAttribute($each, 'pending'));
+ $this->assertEquals(PromiseInterface::FULFILLED, $p->getState());
+ $this->assertEquals([0, 1, 1, 1], $calls);
+ $this->assertFalse($promises->valid());
+ }
+
+ public function testClearsReferencesWhenResolved()
+ {
+ $called = false;
+ $a = new Promise(function () use (&$a, &$called) {
+ $a->resolve('a');
+ $called = true;
+ });
+ $each = new EachPromise([$a], [
+ 'concurrency' => function () { return 1; },
+ 'fulfilled' => function () {},
+ 'rejected' => function () {}
+ ]);
+ $each->promise()->wait();
+ $this->assertNull($this->readAttribute($each, 'onFulfilled'));
+ $this->assertNull($this->readAttribute($each, 'onRejected'));
+ $this->assertNull($this->readAttribute($each, 'iterable'));
+ $this->assertNull($this->readAttribute($each, 'pending'));
+ $this->assertNull($this->readAttribute($each, 'concurrency'));
+ $this->assertTrue($called);
+ }
+
+ public function testCanBeCancelled()
+ {
+ $this->markTestIncomplete();
+ }
+
+ public function testFulfillsImmediatelyWhenGivenAnEmptyIterator()
+ {
+ $each = new EachPromise(new \ArrayIterator([]));
+ $result = $each->promise()->wait();
+ }
+
+ public function testDoesNotBlowStackWithFulfilledPromises()
+ {
+ $pending = [];
+ for ($i = 0; $i < 100; $i++) {
+ $pending[] = new FulfilledPromise($i);
+ }
+ $values = [];
+ $each = new EachPromise($pending, [
+ 'fulfilled' => function ($value) use (&$values) {
+ $values[] = $value;
+ }
+ ]);
+ $called = false;
+ $each->promise()->then(function () use (&$called) {
+ $called = true;
+ });
+ $this->assertFalse($called);
+ P\queue()->run();
+ $this->assertTrue($called);
+ $this->assertEquals(range(0, 99), $values);
+ }
+
+ public function testDoesNotBlowStackWithRejectedPromises()
+ {
+ $pending = [];
+ for ($i = 0; $i < 100; $i++) {
+ $pending[] = new RejectedPromise($i);
+ }
+ $values = [];
+ $each = new EachPromise($pending, [
+ 'rejected' => function ($value) use (&$values) {
+ $values[] = $value;
+ }
+ ]);
+ $called = false;
+ $each->promise()->then(
+ function () use (&$called) { $called = true; },
+ function () { $this->fail('Should not have rejected.'); }
+ );
+ $this->assertFalse($called);
+ P\queue()->run();
+ $this->assertTrue($called);
+ $this->assertEquals(range(0, 99), $values);
+ }
+
+ public function testReturnsPromiseForWhatever()
+ {
+ $called = [];
+ $arr = ['a', 'b'];
+ $each = new EachPromise($arr, [
+ 'fulfilled' => function ($v) use (&$called) { $called[] = $v; }
+ ]);
+ $p = $each->promise();
+ $this->assertNull($p->wait());
+ $this->assertEquals(['a', 'b'], $called);
+ }
+
+ public function testRejectsAggregateWhenNextThrows()
+ {
+ $iter = function () {
+ yield 'a';
+ throw new \Exception('Failure');
+ };
+ $each = new EachPromise($iter());
+ $p = $each->promise();
+ $e = null;
+ $received = null;
+ $p->then(null, function ($reason) use (&$e) { $e = $reason; });
+ P\queue()->run();
+ $this->assertInstanceOf('Exception', $e);
+ $this->assertEquals('Failure', $e->getMessage());
+ }
+
+ public function testDoesNotCallNextOnIteratorUntilNeededWhenWaiting()
+ {
+ $results = [];
+ $values = [10];
+ $remaining = 9;
+ $iter = function () use (&$values) {
+ while ($value = array_pop($values)) {
+ yield $value;
+ }
+ };
+ $each = new EachPromise($iter(), [
+ 'concurrency' => 1,
+ 'fulfilled' => function ($r) use (&$results, &$values, &$remaining) {
+ $results[] = $r;
+ if ($remaining > 0) {
+ $values[] = $remaining--;
+ }
+ }
+ ]);
+ $each->promise()->wait();
+ $this->assertEquals(range(10, 1), $results);
+ }
+
+ public function testDoesNotCallNextOnIteratorUntilNeededWhenAsync()
+ {
+ $firstPromise = new Promise();
+ $pending = [$firstPromise];
+ $values = [$firstPromise];
+ $results = [];
+ $remaining = 9;
+ $iter = function () use (&$values) {
+ while ($value = array_pop($values)) {
+ yield $value;
+ }
+ };
+ $each = new EachPromise($iter(), [
+ 'concurrency' => 1,
+ 'fulfilled' => function ($r) use (&$results, &$values, &$remaining, &$pending) {
+ $results[] = $r;
+ if ($remaining-- > 0) {
+ $pending[] = $values[] = new Promise();
+ }
+ }
+ ]);
+ $i = 0;
+ $each->promise();
+ while ($promise = array_pop($pending)) {
+ $promise->resolve($i++);
+ P\queue()->run();
+ }
+ $this->assertEquals(range(0, 9), $results);
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/tests/FulfilledPromiseTest.php b/server/vendor/guzzlehttp/promises/tests/FulfilledPromiseTest.php
new file mode 100644
index 0000000..554c150
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/FulfilledPromiseTest.php
@@ -0,0 +1,108 @@
+<?php
+namespace GuzzleHttp\Tests\Promise;
+
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\FulfilledPromise;
+
+/**
+ * @covers GuzzleHttp\Promise\FulfilledPromise
+ */
+class FulfilledPromiseTest extends \PHPUnit_Framework_TestCase
+{
+ public function testReturnsValueWhenWaitedUpon()
+ {
+ $p = new FulfilledPromise('foo');
+ $this->assertEquals('fulfilled', $p->getState());
+ $this->assertEquals('foo', $p->wait(true));
+ }
+
+ public function testCannotCancel()
+ {
+ $p = new FulfilledPromise('foo');
+ $this->assertEquals('fulfilled', $p->getState());
+ $p->cancel();
+ $this->assertEquals('foo', $p->wait());
+ }
+
+ /**
+ * @expectedException \LogicException
+ * @exepctedExceptionMessage Cannot resolve a fulfilled promise
+ */
+ public function testCannotResolve()
+ {
+ $p = new FulfilledPromise('foo');
+ $p->resolve('bar');
+ }
+
+ /**
+ * @expectedException \LogicException
+ * @exepctedExceptionMessage Cannot reject a fulfilled promise
+ */
+ public function testCannotReject()
+ {
+ $p = new FulfilledPromise('foo');
+ $p->reject('bar');
+ }
+
+ public function testCanResolveWithSameValue()
+ {
+ $p = new FulfilledPromise('foo');
+ $p->resolve('foo');
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testCannotResolveWithPromise()
+ {
+ new FulfilledPromise(new Promise());
+ }
+
+ public function testReturnsSelfWhenNoOnFulfilled()
+ {
+ $p = new FulfilledPromise('a');
+ $this->assertSame($p, $p->then());
+ }
+
+ public function testAsynchronouslyInvokesOnFulfilled()
+ {
+ $p = new FulfilledPromise('a');
+ $r = null;
+ $f = function ($d) use (&$r) { $r = $d; };
+ $p2 = $p->then($f);
+ $this->assertNotSame($p, $p2);
+ $this->assertNull($r);
+ \GuzzleHttp\Promise\queue()->run();
+ $this->assertEquals('a', $r);
+ }
+
+ public function testReturnsNewRejectedWhenOnFulfilledFails()
+ {
+ $p = new FulfilledPromise('a');
+ $f = function () { throw new \Exception('b'); };
+ $p2 = $p->then($f);
+ $this->assertNotSame($p, $p2);
+ try {
+ $p2->wait();
+ $this->fail();
+ } catch (\Exception $e) {
+ $this->assertEquals('b', $e->getMessage());
+ }
+ }
+
+ public function testOtherwiseIsSugarForRejections()
+ {
+ $c = null;
+ $p = new FulfilledPromise('foo');
+ $p->otherwise(function ($v) use (&$c) { $c = $v; });
+ $this->assertNull($c);
+ }
+
+ public function testDoesNotTryToFulfillTwiceDuringTrampoline()
+ {
+ $fp = new FulfilledPromise('a');
+ $t1 = $fp->then(function ($v) { return $v . ' b'; });
+ $t1->resolve('why!');
+ $this->assertEquals('why!', $t1->wait());
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/tests/NotPromiseInstance.php b/server/vendor/guzzlehttp/promises/tests/NotPromiseInstance.php
new file mode 100644
index 0000000..6288aa8
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/NotPromiseInstance.php
@@ -0,0 +1,50 @@
+<?php
+namespace GuzzleHttp\Promise\Tests;
+
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\PromiseInterface;
+
+class NotPromiseInstance extends Thennable implements PromiseInterface
+{
+ private $nextPromise = null;
+
+ public function __construct()
+ {
+ $this->nextPromise = new Promise();
+ }
+
+ public function then(callable $res = null, callable $rej = null)
+ {
+ return $this->nextPromise->then($res, $rej);
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this->then($onRejected);
+ }
+
+ public function resolve($value)
+ {
+ $this->nextPromise->resolve($value);
+ }
+
+ public function reject($reason)
+ {
+ $this->nextPromise->reject($reason);
+ }
+
+ public function wait($unwrap = true, $defaultResolution = null)
+ {
+
+ }
+
+ public function cancel()
+ {
+
+ }
+
+ public function getState()
+ {
+ return $this->nextPromise->getState();
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/tests/PromiseTest.php b/server/vendor/guzzlehttp/promises/tests/PromiseTest.php
new file mode 100644
index 0000000..946c627
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/PromiseTest.php
@@ -0,0 +1,579 @@
+<?php
+namespace GuzzleHttp\Promise\Tests;
+
+use GuzzleHttp\Promise\CancellationException;
+use GuzzleHttp\Promise as P;
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\RejectedPromise;
+use GuzzleHttp\Promise\RejectionException;
+
+/**
+ * @covers GuzzleHttp\Promise\Promise
+ */
+class PromiseTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @expectedException \LogicException
+ * @expectedExceptionMessage The promise is already fulfilled
+ */
+ public function testCannotResolveNonPendingPromise()
+ {
+ $p = new Promise();
+ $p->resolve('foo');
+ $p->resolve('bar');
+ $this->assertEquals('foo', $p->wait());
+ }
+
+ public function testCanResolveWithSameValue()
+ {
+ $p = new Promise();
+ $p->resolve('foo');
+ $p->resolve('foo');
+ }
+
+ /**
+ * @expectedException \LogicException
+ * @expectedExceptionMessage Cannot change a fulfilled promise to rejected
+ */
+ public function testCannotRejectNonPendingPromise()
+ {
+ $p = new Promise();
+ $p->resolve('foo');
+ $p->reject('bar');
+ $this->assertEquals('foo', $p->wait());
+ }
+
+ public function testCanRejectWithSameValue()
+ {
+ $p = new Promise();
+ $p->reject('foo');
+ $p->reject('foo');
+ }
+
+ /**
+ * @expectedException \LogicException
+ * @expectedExceptionMessage Cannot change a fulfilled promise to rejected
+ */
+ public function testCannotRejectResolveWithSameValue()
+ {
+ $p = new Promise();
+ $p->resolve('foo');
+ $p->reject('foo');
+ }
+
+ public function testInvokesWaitFunction()
+ {
+ $p = new Promise(function () use (&$p) { $p->resolve('10'); });
+ $this->assertEquals('10', $p->wait());
+ }
+
+ /**
+ * @expectedException \GuzzleHttp\Promise\RejectionException
+ */
+ public function testRejectsAndThrowsWhenWaitFailsToResolve()
+ {
+ $p = new Promise(function () {});
+ $p->wait();
+ }
+
+ /**
+ * @expectedException \GuzzleHttp\Promise\RejectionException
+ * @expectedExceptionMessage The promise was rejected with reason: foo
+ */
+ public function testThrowsWhenUnwrapIsRejectedWithNonException()
+ {
+ $p = new Promise(function () use (&$p) { $p->reject('foo'); });
+ $p->wait();
+ }
+
+ /**
+ * @expectedException \UnexpectedValueException
+ * @expectedExceptionMessage foo
+ */
+ public function testThrowsWhenUnwrapIsRejectedWithException()
+ {
+ $e = new \UnexpectedValueException('foo');
+ $p = new Promise(function () use (&$p, $e) { $p->reject($e); });
+ $p->wait();
+ }
+
+ public function testDoesNotUnwrapExceptionsWhenDisabled()
+ {
+ $p = new Promise(function () use (&$p) { $p->reject('foo'); });
+ $this->assertEquals('pending', $p->getState());
+ $p->wait(false);
+ $this->assertEquals('rejected', $p->getState());
+ }
+
+ public function testRejectsSelfWhenWaitThrows()
+ {
+ $e = new \UnexpectedValueException('foo');
+ $p = new Promise(function () use ($e) { throw $e; });
+ try {
+ $p->wait();
+ $this->fail();
+ } catch (\UnexpectedValueException $e) {
+ $this->assertEquals('rejected', $p->getState());
+ }
+ }
+
+ public function testWaitsOnNestedPromises()
+ {
+ $p = new Promise(function () use (&$p) { $p->resolve('_'); });
+ $p2 = new Promise(function () use (&$p2) { $p2->resolve('foo'); });
+ $p3 = $p->then(function () use ($p2) { return $p2; });
+ $this->assertSame('foo', $p3->wait());
+ }
+
+ /**
+ * @expectedException \GuzzleHttp\Promise\RejectionException
+ */
+ public function testThrowsWhenWaitingOnPromiseWithNoWaitFunction()
+ {
+ $p = new Promise();
+ $p->wait();
+ }
+
+ public function testThrowsWaitExceptionAfterPromiseIsResolved()
+ {
+ $p = new Promise(function () use (&$p) {
+ $p->reject('Foo!');
+ throw new \Exception('Bar?');
+ });
+
+ try {
+ $p->wait();
+ $this->fail();
+ } catch (\Exception $e) {
+ $this->assertEquals('Bar?', $e->getMessage());
+ }
+ }
+
+ public function testGetsActualWaitValueFromThen()
+ {
+ $p = new Promise(function () use (&$p) { $p->reject('Foo!'); });
+ $p2 = $p->then(null, function ($reason) {
+ return new RejectedPromise([$reason]);
+ });
+
+ try {
+ $p2->wait();
+ $this->fail('Should have thrown');
+ } catch (RejectionException $e) {
+ $this->assertEquals(['Foo!'], $e->getReason());
+ }
+ }
+
+ public function testWaitBehaviorIsBasedOnLastPromiseInChain()
+ {
+ $p3 = new Promise(function () use (&$p3) { $p3->resolve('Whoop'); });
+ $p2 = new Promise(function () use (&$p2, $p3) { $p2->reject($p3); });
+ $p = new Promise(function () use (&$p, $p2) { $p->reject($p2); });
+ $this->assertEquals('Whoop', $p->wait());
+ }
+
+ public function testCannotCancelNonPending()
+ {
+ $p = new Promise();
+ $p->resolve('foo');
+ $p->cancel();
+ $this->assertEquals('fulfilled', $p->getState());
+ }
+
+ /**
+ * @expectedException \GuzzleHttp\Promise\CancellationException
+ */
+ public function testCancelsPromiseWhenNoCancelFunction()
+ {
+ $p = new Promise();
+ $p->cancel();
+ $this->assertEquals('rejected', $p->getState());
+ $p->wait();
+ }
+
+ public function testCancelsPromiseWithCancelFunction()
+ {
+ $called = false;
+ $p = new Promise(null, function () use (&$called) { $called = true; });
+ $p->cancel();
+ $this->assertEquals('rejected', $p->getState());
+ $this->assertTrue($called);
+ }
+
+ public function testCancelsUppermostPendingPromise()
+ {
+ $called = false;
+ $p1 = new Promise(null, function () use (&$called) { $called = true; });
+ $p2 = $p1->then(function () {});
+ $p3 = $p2->then(function () {});
+ $p4 = $p3->then(function () {});
+ $p3->cancel();
+ $this->assertEquals('rejected', $p1->getState());
+ $this->assertEquals('rejected', $p2->getState());
+ $this->assertEquals('rejected', $p3->getState());
+ $this->assertEquals('pending', $p4->getState());
+ $this->assertTrue($called);
+
+ try {
+ $p3->wait();
+ $this->fail();
+ } catch (CancellationException $e) {
+ $this->assertContains('cancelled', $e->getMessage());
+ }
+
+ try {
+ $p4->wait();
+ $this->fail();
+ } catch (CancellationException $e) {
+ $this->assertContains('cancelled', $e->getMessage());
+ }
+
+ $this->assertEquals('rejected', $p4->getState());
+ }
+
+ public function testCancelsChildPromises()
+ {
+ $called1 = $called2 = $called3 = false;
+ $p1 = new Promise(null, function () use (&$called1) { $called1 = true; });
+ $p2 = new Promise(null, function () use (&$called2) { $called2 = true; });
+ $p3 = new Promise(null, function () use (&$called3) { $called3 = true; });
+ $p4 = $p2->then(function () use ($p3) { return $p3; });
+ $p5 = $p4->then(function () { $this->fail(); });
+ $p4->cancel();
+ $this->assertEquals('pending', $p1->getState());
+ $this->assertEquals('rejected', $p2->getState());
+ $this->assertEquals('rejected', $p4->getState());
+ $this->assertEquals('pending', $p5->getState());
+ $this->assertFalse($called1);
+ $this->assertTrue($called2);
+ $this->assertFalse($called3);
+ }
+
+ public function testRejectsPromiseWhenCancelFails()
+ {
+ $called = false;
+ $p = new Promise(null, function () use (&$called) {
+ $called = true;
+ throw new \Exception('e');
+ });
+ $p->cancel();
+ $this->assertEquals('rejected', $p->getState());
+ $this->assertTrue($called);
+ try {
+ $p->wait();
+ $this->fail();
+ } catch (\Exception $e) {
+ $this->assertEquals('e', $e->getMessage());
+ }
+ }
+
+ public function testCreatesPromiseWhenFulfilledAfterThen()
+ {
+ $p = new Promise();
+ $carry = null;
+ $p2 = $p->then(function ($v) use (&$carry) { $carry = $v; });
+ $this->assertNotSame($p, $p2);
+ $p->resolve('foo');
+ P\queue()->run();
+
+ $this->assertEquals('foo', $carry);
+ }
+
+ public function testCreatesPromiseWhenFulfilledBeforeThen()
+ {
+ $p = new Promise();
+ $p->resolve('foo');
+ $carry = null;
+ $p2 = $p->then(function ($v) use (&$carry) { $carry = $v; });
+ $this->assertNotSame($p, $p2);
+ $this->assertNull($carry);
+ \GuzzleHttp\Promise\queue()->run();
+ $this->assertEquals('foo', $carry);
+ }
+
+ public function testCreatesPromiseWhenFulfilledWithNoCallback()
+ {
+ $p = new Promise();
+ $p->resolve('foo');
+ $p2 = $p->then();
+ $this->assertNotSame($p, $p2);
+ $this->assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $p2);
+ }
+
+ public function testCreatesPromiseWhenRejectedAfterThen()
+ {
+ $p = new Promise();
+ $carry = null;
+ $p2 = $p->then(null, function ($v) use (&$carry) { $carry = $v; });
+ $this->assertNotSame($p, $p2);
+ $p->reject('foo');
+ P\queue()->run();
+ $this->assertEquals('foo', $carry);
+ }
+
+ public function testCreatesPromiseWhenRejectedBeforeThen()
+ {
+ $p = new Promise();
+ $p->reject('foo');
+ $carry = null;
+ $p2 = $p->then(null, function ($v) use (&$carry) { $carry = $v; });
+ $this->assertNotSame($p, $p2);
+ $this->assertNull($carry);
+ P\queue()->run();
+ $this->assertEquals('foo', $carry);
+ }
+
+ public function testCreatesPromiseWhenRejectedWithNoCallback()
+ {
+ $p = new Promise();
+ $p->reject('foo');
+ $p2 = $p->then();
+ $this->assertNotSame($p, $p2);
+ $this->assertInstanceOf('GuzzleHttp\Promise\RejectedPromise', $p2);
+ }
+
+ public function testInvokesWaitFnsForThens()
+ {
+ $p = new Promise(function () use (&$p) { $p->resolve('a'); });
+ $p2 = $p
+ ->then(function ($v) { return $v . '-1-'; })
+ ->then(function ($v) { return $v . '2'; });
+ $this->assertEquals('a-1-2', $p2->wait());
+ }
+
+ public function testStacksThenWaitFunctions()
+ {
+ $p1 = new Promise(function () use (&$p1) { $p1->resolve('a'); });
+ $p2 = new Promise(function () use (&$p2) { $p2->resolve('b'); });
+ $p3 = new Promise(function () use (&$p3) { $p3->resolve('c'); });
+ $p4 = $p1
+ ->then(function () use ($p2) { return $p2; })
+ ->then(function () use ($p3) { return $p3; });
+ $this->assertEquals('c', $p4->wait());
+ }
+
+ public function testForwardsFulfilledDownChainBetweenGaps()
+ {
+ $p = new Promise();
+ $r = $r2 = null;
+ $p->then(null, null)
+ ->then(function ($v) use (&$r) { $r = $v; return $v . '2'; })
+ ->then(function ($v) use (&$r2) { $r2 = $v; });
+ $p->resolve('foo');
+ P\queue()->run();
+ $this->assertEquals('foo', $r);
+ $this->assertEquals('foo2', $r2);
+ }
+
+ public function testForwardsRejectedPromisesDownChainBetweenGaps()
+ {
+ $p = new Promise();
+ $r = $r2 = null;
+ $p->then(null, null)
+ ->then(null, function ($v) use (&$r) { $r = $v; return $v . '2'; })
+ ->then(function ($v) use (&$r2) { $r2 = $v; });
+ $p->reject('foo');
+ P\queue()->run();
+ $this->assertEquals('foo', $r);
+ $this->assertEquals('foo2', $r2);
+ }
+
+ public function testForwardsThrownPromisesDownChainBetweenGaps()
+ {
+ $e = new \Exception();
+ $p = new Promise();
+ $r = $r2 = null;
+ $p->then(null, null)
+ ->then(null, function ($v) use (&$r, $e) {
+ $r = $v;
+ throw $e;
+ })
+ ->then(
+ null,
+ function ($v) use (&$r2) { $r2 = $v; }
+ );
+ $p->reject('foo');
+ P\queue()->run();
+ $this->assertEquals('foo', $r);
+ $this->assertSame($e, $r2);
+ }
+
+ public function testForwardsReturnedRejectedPromisesDownChainBetweenGaps()
+ {
+ $p = new Promise();
+ $rejected = new RejectedPromise('bar');
+ $r = $r2 = null;
+ $p->then(null, null)
+ ->then(null, function ($v) use (&$r, $rejected) {
+ $r = $v;
+ return $rejected;
+ })
+ ->then(
+ null,
+ function ($v) use (&$r2) { $r2 = $v; }
+ );
+ $p->reject('foo');
+ P\queue()->run();
+ $this->assertEquals('foo', $r);
+ $this->assertEquals('bar', $r2);
+ try {
+ $p->wait();
+ } catch (RejectionException $e) {
+ $this->assertEquals('foo', $e->getReason());
+ }
+ }
+
+ public function testForwardsHandlersToNextPromise()
+ {
+ $p = new Promise();
+ $p2 = new Promise();
+ $resolved = null;
+ $p
+ ->then(function ($v) use ($p2) { return $p2; })
+ ->then(function ($value) use (&$resolved) { $resolved = $value; });
+ $p->resolve('a');
+ $p2->resolve('b');
+ P\queue()->run();
+ $this->assertEquals('b', $resolved);
+ }
+
+ public function testRemovesReferenceFromChildWhenParentWaitedUpon()
+ {
+ $r = null;
+ $p = new Promise(function () use (&$p) { $p->resolve('a'); });
+ $p2 = new Promise(function () use (&$p2) { $p2->resolve('b'); });
+ $pb = $p->then(
+ function ($v) use ($p2, &$r) {
+ $r = $v;
+ return $p2;
+ })
+ ->then(function ($v) { return $v . '.'; });
+ $this->assertEquals('a', $p->wait());
+ $this->assertEquals('b', $p2->wait());
+ $this->assertEquals('b.', $pb->wait());
+ $this->assertEquals('a', $r);
+ }
+
+ public function testForwardsHandlersWhenFulfilledPromiseIsReturned()
+ {
+ $res = [];
+ $p = new Promise();
+ $p2 = new Promise();
+ $p2->resolve('foo');
+ $p2->then(function ($v) use (&$res) { $res[] = 'A:' . $v; });
+ // $res is A:foo
+ $p
+ ->then(function () use ($p2, &$res) { $res[] = 'B'; return $p2; })
+ ->then(function ($v) use (&$res) { $res[] = 'C:' . $v; });
+ $p->resolve('a');
+ $p->then(function ($v) use (&$res) { $res[] = 'D:' . $v; });
+ P\queue()->run();
+ $this->assertEquals(['A:foo', 'B', 'D:a', 'C:foo'], $res);
+ }
+
+ public function testForwardsHandlersWhenRejectedPromiseIsReturned()
+ {
+ $res = [];
+ $p = new Promise();
+ $p2 = new Promise();
+ $p2->reject('foo');
+ $p2->then(null, function ($v) use (&$res) { $res[] = 'A:' . $v; });
+ $p->then(null, function () use ($p2, &$res) { $res[] = 'B'; return $p2; })
+ ->then(null, function ($v) use (&$res) { $res[] = 'C:' . $v; });
+ $p->reject('a');
+ $p->then(null, function ($v) use (&$res) { $res[] = 'D:' . $v; });
+ P\queue()->run();
+ $this->assertEquals(['A:foo', 'B', 'D:a', 'C:foo'], $res);
+ }
+
+ public function testDoesNotForwardRejectedPromise()
+ {
+ $res = [];
+ $p = new Promise();
+ $p2 = new Promise();
+ $p2->cancel();
+ $p2->then(function ($v) use (&$res) { $res[] = "B:$v"; return $v; });
+ $p->then(function ($v) use ($p2, &$res) { $res[] = "B:$v"; return $p2; })
+ ->then(function ($v) use (&$res) { $res[] = 'C:' . $v; });
+ $p->resolve('a');
+ $p->then(function ($v) use (&$res) { $res[] = 'D:' . $v; });
+ P\queue()->run();
+ $this->assertEquals(['B:a', 'D:a'], $res);
+ }
+
+ public function testRecursivelyForwardsWhenOnlyThennable()
+ {
+ $res = [];
+ $p = new Promise();
+ $p2 = new Thennable();
+ $p2->resolve('foo');
+ $p2->then(function ($v) use (&$res) { $res[] = 'A:' . $v; });
+ $p->then(function () use ($p2, &$res) { $res[] = 'B'; return $p2; })
+ ->then(function ($v) use (&$res) { $res[] = 'C:' . $v; });
+ $p->resolve('a');
+ $p->then(function ($v) use (&$res) { $res[] = 'D:' . $v; });
+ P\queue()->run();
+ $this->assertEquals(['A:foo', 'B', 'D:a', 'C:foo'], $res);
+ }
+
+ public function testRecursivelyForwardsWhenNotInstanceOfPromise()
+ {
+ $res = [];
+ $p = new Promise();
+ $p2 = new NotPromiseInstance();
+ $p2->then(function ($v) use (&$res) { $res[] = 'A:' . $v; });
+ $p->then(function () use ($p2, &$res) { $res[] = 'B'; return $p2; })
+ ->then(function ($v) use (&$res) { $res[] = 'C:' . $v; });
+ $p->resolve('a');
+ $p->then(function ($v) use (&$res) { $res[] = 'D:' . $v; });
+ P\queue()->run();
+ $this->assertEquals(['B', 'D:a'], $res);
+ $p2->resolve('foo');
+ P\queue()->run();
+ $this->assertEquals(['B', 'D:a', 'A:foo', 'C:foo'], $res);
+ }
+
+ /**
+ * @expectedException \LogicException
+ * @expectedExceptionMessage Cannot fulfill or reject a promise with itself
+ */
+ public function testCannotResolveWithSelf()
+ {
+ $p = new Promise();
+ $p->resolve($p);
+ }
+
+ /**
+ * @expectedException \LogicException
+ * @expectedExceptionMessage Cannot fulfill or reject a promise with itself
+ */
+ public function testCannotRejectWithSelf()
+ {
+ $p = new Promise();
+ $p->reject($p);
+ }
+
+ public function testDoesNotBlowStackWhenWaitingOnNestedThens()
+ {
+ $inner = new Promise(function () use (&$inner) { $inner->resolve(0); });
+ $prev = $inner;
+ for ($i = 1; $i < 100; $i++) {
+ $prev = $prev->then(function ($i) { return $i + 1; });
+ }
+
+ $parent = new Promise(function () use (&$parent, $prev) {
+ $parent->resolve($prev);
+ });
+
+ $this->assertEquals(99, $parent->wait());
+ }
+
+ public function testOtherwiseIsSugarForRejections()
+ {
+ $p = new Promise();
+ $p->reject('foo');
+ $p->otherwise(function ($v) use (&$c) { $c = $v; });
+ P\queue()->run();
+ $this->assertEquals($c, 'foo');
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/tests/RejectedPromiseTest.php b/server/vendor/guzzlehttp/promises/tests/RejectedPromiseTest.php
new file mode 100644
index 0000000..60f926e
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/RejectedPromiseTest.php
@@ -0,0 +1,143 @@
+<?php
+namespace GuzzleHttp\Promise\Tests;
+
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\RejectedPromise;
+
+/**
+ * @covers GuzzleHttp\Promise\RejectedPromise
+ */
+class RejectedPromiseTest extends \PHPUnit_Framework_TestCase
+{
+ public function testThrowsReasonWhenWaitedUpon()
+ {
+ $p = new RejectedPromise('foo');
+ $this->assertEquals('rejected', $p->getState());
+ try {
+ $p->wait(true);
+ $this->fail();
+ } catch (\Exception $e) {
+ $this->assertEquals('rejected', $p->getState());
+ $this->assertContains('foo', $e->getMessage());
+ }
+ }
+
+ public function testCannotCancel()
+ {
+ $p = new RejectedPromise('foo');
+ $p->cancel();
+ $this->assertEquals('rejected', $p->getState());
+ }
+
+ /**
+ * @expectedException \LogicException
+ * @exepctedExceptionMessage Cannot resolve a rejected promise
+ */
+ public function testCannotResolve()
+ {
+ $p = new RejectedPromise('foo');
+ $p->resolve('bar');
+ }
+
+ /**
+ * @expectedException \LogicException
+ * @exepctedExceptionMessage Cannot reject a rejected promise
+ */
+ public function testCannotReject()
+ {
+ $p = new RejectedPromise('foo');
+ $p->reject('bar');
+ }
+
+ public function testCanRejectWithSameValue()
+ {
+ $p = new RejectedPromise('foo');
+ $p->reject('foo');
+ }
+
+ public function testThrowsSpecificException()
+ {
+ $e = new \Exception();
+ $p = new RejectedPromise($e);
+ try {
+ $p->wait(true);
+ $this->fail();
+ } catch (\Exception $e2) {
+ $this->assertSame($e, $e2);
+ }
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testCannotResolveWithPromise()
+ {
+ new RejectedPromise(new Promise());
+ }
+
+ public function testReturnsSelfWhenNoOnReject()
+ {
+ $p = new RejectedPromise('a');
+ $this->assertSame($p, $p->then());
+ }
+
+ public function testInvokesOnRejectedAsynchronously()
+ {
+ $p = new RejectedPromise('a');
+ $r = null;
+ $f = function ($reason) use (&$r) { $r = $reason; };
+ $p->then(null, $f);
+ $this->assertNull($r);
+ \GuzzleHttp\Promise\queue()->run();
+ $this->assertEquals('a', $r);
+ }
+
+ public function testReturnsNewRejectedWhenOnRejectedFails()
+ {
+ $p = new RejectedPromise('a');
+ $f = function () { throw new \Exception('b'); };
+ $p2 = $p->then(null, $f);
+ $this->assertNotSame($p, $p2);
+ try {
+ $p2->wait();
+ $this->fail();
+ } catch (\Exception $e) {
+ $this->assertEquals('b', $e->getMessage());
+ }
+ }
+
+ public function testWaitingIsNoOp()
+ {
+ $p = new RejectedPromise('a');
+ $p->wait(false);
+ }
+
+ public function testOtherwiseIsSugarForRejections()
+ {
+ $p = new RejectedPromise('foo');
+ $p->otherwise(function ($v) use (&$c) { $c = $v; });
+ \GuzzleHttp\Promise\queue()->run();
+ $this->assertSame('foo', $c);
+ }
+
+ public function testCanResolveThenWithSuccess()
+ {
+ $actual = null;
+ $p = new RejectedPromise('foo');
+ $p->otherwise(function ($v) {
+ return $v . ' bar';
+ })->then(function ($v) use (&$actual) {
+ $actual = $v;
+ });
+ \GuzzleHttp\Promise\queue()->run();
+ $this->assertEquals('foo bar', $actual);
+ }
+
+ public function testDoesNotTryToRejectTwiceDuringTrampoline()
+ {
+ $fp = new RejectedPromise('a');
+ $t1 = $fp->then(null, function ($v) { return $v . ' b'; });
+ $t1->resolve('why!');
+ $this->assertEquals('why!', $t1->wait());
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/tests/RejectionExceptionTest.php b/server/vendor/guzzlehttp/promises/tests/RejectionExceptionTest.php
new file mode 100644
index 0000000..36c6a88
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/RejectionExceptionTest.php
@@ -0,0 +1,47 @@
+<?php
+namespace GuzzleHttp\Promise\Tests;
+
+use GuzzleHttp\Promise\RejectionException;
+
+class Thing1
+{
+ public function __construct($message)
+ {
+ $this->message = $message;
+ }
+
+ public function __toString()
+ {
+ return $this->message;
+ }
+}
+
+class Thing2 implements \JsonSerializable
+{
+ public function jsonSerialize()
+ {
+ return '{}';
+ }
+}
+
+/**
+ * @covers GuzzleHttp\Promise\RejectionException
+ */
+class RejectionExceptionTest extends \PHPUnit_Framework_TestCase
+{
+ public function testCanGetReasonFromException()
+ {
+ $thing = new Thing1('foo');
+ $e = new RejectionException($thing);
+
+ $this->assertSame($thing, $e->getReason());
+ $this->assertEquals('The promise was rejected with reason: foo', $e->getMessage());
+ }
+
+ public function testCanGetReasonMessageFromJson()
+ {
+ $reason = new Thing2();
+ $e = new RejectionException($reason);
+ $this->assertContains("{}", $e->getMessage());
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/tests/TaskQueueTest.php b/server/vendor/guzzlehttp/promises/tests/TaskQueueTest.php
new file mode 100644
index 0000000..845b263
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/TaskQueueTest.php
@@ -0,0 +1,31 @@
+<?php
+namespace GuzzleHttp\Promise\Test;
+
+use GuzzleHttp\Promise\TaskQueue;
+
+class TaskQueueTest extends \PHPUnit_Framework_TestCase
+{
+ public function testKnowsIfEmpty()
+ {
+ $tq = new TaskQueue(false);
+ $this->assertTrue($tq->isEmpty());
+ }
+
+ public function testKnowsIfFull()
+ {
+ $tq = new TaskQueue(false);
+ $tq->add(function () {});
+ $this->assertFalse($tq->isEmpty());
+ }
+
+ public function testExecutesTasksInOrder()
+ {
+ $tq = new TaskQueue(false);
+ $called = [];
+ $tq->add(function () use (&$called) { $called[] = 'a'; });
+ $tq->add(function () use (&$called) { $called[] = 'b'; });
+ $tq->add(function () use (&$called) { $called[] = 'c'; });
+ $tq->run();
+ $this->assertEquals(['a', 'b', 'c'], $called);
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/tests/Thennable.php b/server/vendor/guzzlehttp/promises/tests/Thennable.php
new file mode 100644
index 0000000..398954d
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/Thennable.php
@@ -0,0 +1,24 @@
+<?php
+namespace GuzzleHttp\Promise\Tests;
+
+use GuzzleHttp\Promise\Promise;
+
+class Thennable
+{
+ private $nextPromise = null;
+
+ public function __construct()
+ {
+ $this->nextPromise = new Promise();
+ }
+
+ public function then(callable $res = null, callable $rej = null)
+ {
+ return $this->nextPromise->then($res, $rej);
+ }
+
+ public function resolve($value)
+ {
+ $this->nextPromise->resolve($value);
+ }
+}
diff --git a/server/vendor/guzzlehttp/promises/tests/bootstrap.php b/server/vendor/guzzlehttp/promises/tests/bootstrap.php
new file mode 100644
index 0000000..a63d264
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/bootstrap.php
@@ -0,0 +1,4 @@
+<?php
+require __DIR__ . '/../vendor/autoload.php';
+require __DIR__ . '/Thennable.php';
+require __DIR__ . '/NotPromiseInstance.php';
diff --git a/server/vendor/guzzlehttp/promises/tests/functionsTest.php b/server/vendor/guzzlehttp/promises/tests/functionsTest.php
new file mode 100644
index 0000000..8e6fcf4
--- /dev/null
+++ b/server/vendor/guzzlehttp/promises/tests/functionsTest.php
@@ -0,0 +1,694 @@
+<?php
+namespace GuzzleHttp\Promise\Tests;
+
+use GuzzleHttp\Promise as P;
+use GuzzleHttp\Promise\FulfilledPromise;
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\RejectedPromise;
+
+class FunctionsTest extends \PHPUnit_Framework_TestCase
+{
+ public function testCreatesPromiseForValue()
+ {
+ $p = \GuzzleHttp\Promise\promise_for('foo');
+ $this->assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $p);
+ }
+
+ public function testReturnsPromiseForPromise()
+ {
+ $p = new Promise();
+ $this->assertSame($p, \GuzzleHttp\Promise\promise_for($p));
+ }
+
+ public function testReturnsPromiseForThennable()
+ {
+ $p = new Thennable();
+ $wrapped = \GuzzleHttp\Promise\promise_for($p);
+ $this->assertNotSame($p, $wrapped);
+ $this->assertInstanceOf('GuzzleHttp\Promise\PromiseInterface', $wrapped);
+ $p->resolve('foo');
+ P\queue()->run();
+ $this->assertEquals('foo', $wrapped->wait());
+ }
+
+ public function testReturnsRejection()
+ {
+ $p = \GuzzleHttp\Promise\rejection_for('fail');
+ $this->assertInstanceOf('GuzzleHttp\Promise\RejectedPromise', $p);
+ $this->assertEquals('fail', $this->readAttribute($p, 'reason'));
+ }
+
+ public function testReturnsPromisesAsIsInRejectionFor()
+ {
+ $a = new Promise();
+ $b = \GuzzleHttp\Promise\rejection_for($a);
+ $this->assertSame($a, $b);
+ }
+
+ public function testWaitsOnAllPromisesIntoArray()
+ {
+ $e = new \Exception();
+ $a = new Promise(function () use (&$a) { $a->resolve('a'); });
+ $b = new Promise(function () use (&$b) { $b->reject('b'); });
+ $c = new Promise(function () use (&$c, $e) { $c->reject($e); });
+ $results = \GuzzleHttp\Promise\inspect_all([$a, $b, $c]);
+ $this->assertEquals([
+ ['state' => 'fulfilled', 'value' => 'a'],
+ ['state' => 'rejected', 'reason' => 'b'],
+ ['state' => 'rejected', 'reason' => $e]
+ ], $results);
+ }
+
+ /**
+ * @expectedException \GuzzleHttp\Promise\RejectionException
+ */
+ public function testUnwrapsPromisesWithNoDefaultAndFailure()
+ {
+ $promises = [new FulfilledPromise('a'), new Promise()];
+ \GuzzleHttp\Promise\unwrap($promises);
+ }
+
+ public function testUnwrapsPromisesWithNoDefault()
+ {
+ $promises = [new FulfilledPromise('a')];
+ $this->assertEquals(['a'], \GuzzleHttp\Promise\unwrap($promises));
+ }
+
+ public function testUnwrapsPromisesWithKeys()
+ {
+ $promises = [
+ 'foo' => new FulfilledPromise('a'),
+ 'bar' => new FulfilledPromise('b'),
+ ];
+ $this->assertEquals([
+ 'foo' => 'a',
+ 'bar' => 'b'
+ ], \GuzzleHttp\Promise\unwrap($promises));
+ }
+
+ public function testAllAggregatesSortedArray()
+ {
+ $a = new Promise();
+ $b = new Promise();
+ $c = new Promise();
+ $d = \GuzzleHttp\Promise\all([$a, $b, $c]);
+ $b->resolve('b');
+ $a->resolve('a');
+ $c->resolve('c');
+ $d->then(
+ function ($value) use (&$result) { $result = $value; },
+ function ($reason) use (&$result) { $result = $reason; }
+ );
+ P\queue()->run();
+ $this->assertEquals(['a', 'b', 'c'], $result);
+ }
+
+ public function testAllThrowsWhenAnyRejected()
+ {
+ $a = new Promise();
+ $b = new Promise();
+ $c = new Promise();
+ $d = \GuzzleHttp\Promise\all([$a, $b, $c]);
+ $b->resolve('b');
+ $a->reject('fail');
+ $c->resolve('c');
+ $d->then(
+ function ($value) use (&$result) { $result = $value; },
+ function ($reason) use (&$result) { $result = $reason; }
+ );
+ P\queue()->run();
+ $this->assertEquals('fail', $result);
+ }
+
+ public function testSomeAggregatesSortedArrayWithMax()
+ {
+ $a = new Promise();
+ $b = new Promise();
+ $c = new Promise();
+ $d = \GuzzleHttp\Promise\some(2, [$a, $b, $c]);
+ $b->resolve('b');
+ $c->resolve('c');
+ $a->resolve('a');
+ $d->then(function ($value) use (&$result) { $result = $value; });
+ P\queue()->run();
+ $this->assertEquals(['b', 'c'], $result);
+ }
+
+ public function testSomeRejectsWhenTooManyRejections()
+ {
+ $a = new Promise();
+ $b = new Promise();
+ $d = \GuzzleHttp\Promise\some(2, [$a, $b]);
+ $a->reject('bad');
+ $b->resolve('good');
+ P\queue()->run();
+ $this->assertEquals($a::REJECTED, $d->getState());
+ $d->then(null, function ($reason) use (&$called) {
+ $called = $reason;
+ });
+ P\queue()->run();
+ $this->assertInstanceOf('GuzzleHttp\Promise\AggregateException', $called);
+ $this->assertContains('bad', $called->getReason());
+ }
+
+ public function testCanWaitUntilSomeCountIsSatisfied()
+ {
+ $a = new Promise(function () use (&$a) { $a->resolve('a'); });
+ $b = new Promise(function () use (&$b) { $b->resolve('b'); });
+ $c = new Promise(function () use (&$c) { $c->resolve('c'); });
+ $d = \GuzzleHttp\Promise\some(2, [$a, $b, $c]);
+ $this->assertEquals(['a', 'b'], $d->wait());
+ }
+
+ /**
+ * @expectedException \GuzzleHttp\Promise\AggregateException
+ * @expectedExceptionMessage Not enough promises to fulfill count
+ */
+ public function testThrowsIfImpossibleToWaitForSomeCount()
+ {
+ $a = new Promise(function () use (&$a) { $a->resolve('a'); });
+ $d = \GuzzleHttp\Promise\some(2, [$a]);
+ $d->wait();
+ }
+
+ /**
+ * @expectedException \GuzzleHttp\Promise\AggregateException
+ * @expectedExceptionMessage Not enough promises to fulfill count
+ */
+ public function testThrowsIfResolvedWithoutCountTotalResults()
+ {
+ $a = new Promise();
+ $b = new Promise();
+ $d = \GuzzleHttp\Promise\some(3, [$a, $b]);
+ $a->resolve('a');
+ $b->resolve('b');
+ $d->wait();
+ }
+
+ public function testAnyReturnsFirstMatch()
+ {
+ $a = new Promise();
+ $b = new Promise();
+ $c = \GuzzleHttp\Promise\any([$a, $b]);
+ $b->resolve('b');
+ $a->resolve('a');
+ //P\queue()->run();
+ //$this->assertEquals('fulfilled', $c->getState());
+ $c->then(function ($value) use (&$result) { $result = $value; });
+ P\queue()->run();
+ $this->assertEquals('b', $result);
+ }
+
+ public function testSettleFulfillsWithFulfilledAndRejected()
+ {
+ $a = new Promise();
+ $b = new Promise();
+ $c = new Promise();
+ $d = \GuzzleHttp\Promise\settle([$a, $b, $c]);
+ $b->resolve('b');
+ $c->resolve('c');
+ $a->reject('a');
+ P\queue()->run();
+ $this->assertEquals('fulfilled', $d->getState());
+ $d->then(function ($value) use (&$result) { $result = $value; });
+ P\queue()->run();
+ $this->assertEquals([
+ ['state' => 'rejected', 'reason' => 'a'],
+ ['state' => 'fulfilled', 'value' => 'b'],
+ ['state' => 'fulfilled', 'value' => 'c']
+ ], $result);
+ }
+
+ public function testCanInspectFulfilledPromise()
+ {
+ $p = new FulfilledPromise('foo');
+ $this->assertEquals([
+ 'state' => 'fulfilled',
+ 'value' => 'foo'
+ ], \GuzzleHttp\Promise\inspect($p));
+ }
+
+ public function testCanInspectRejectedPromise()
+ {
+ $p = new RejectedPromise('foo');
+ $this->assertEquals([
+ 'state' => 'rejected',
+ 'reason' => 'foo'
+ ], \GuzzleHttp\Promise\inspect($p));
+ }
+
+ public function testCanInspectRejectedPromiseWithNormalException()
+ {
+ $e = new \Exception('foo');
+ $p = new RejectedPromise($e);
+ $this->assertEquals([
+ 'state' => 'rejected',
+ 'reason' => $e
+ ], \GuzzleHttp\Promise\inspect($p));
+ }
+
+ public function testCallsEachLimit()
+ {
+ $p = new Promise();
+ $aggregate = \GuzzleHttp\Promise\each_limit($p, 2);
+ $p->resolve('a');
+ P\queue()->run();
+ $this->assertEquals($p::FULFILLED, $aggregate->getState());
+ }
+
+ public function testEachLimitAllRejectsOnFailure()
+ {
+ $p = [new FulfilledPromise('a'), new RejectedPromise('b')];
+ $aggregate = \GuzzleHttp\Promise\each_limit_all($p, 2);
+ P\queue()->run();
+ $this->assertEquals(P\PromiseInterface::REJECTED, $aggregate->getState());
+ $result = \GuzzleHttp\Promise\inspect($aggregate);
+ $this->assertEquals('b', $result['reason']);
+ }
+
+ public function testIterForReturnsIterator()
+ {
+ $iter = new \ArrayIterator();
+ $this->assertSame($iter, \GuzzleHttp\Promise\iter_for($iter));
+ }
+
+ public function testKnowsIfFulfilled()
+ {
+ $p = new FulfilledPromise(null);
+ $this->assertTrue(P\is_fulfilled($p));
+ $this->assertFalse(P\is_rejected($p));
+ }
+
+ public function testKnowsIfRejected()
+ {
+ $p = new RejectedPromise(null);
+ $this->assertTrue(P\is_rejected($p));
+ $this->assertFalse(P\is_fulfilled($p));
+ }
+
+ public function testKnowsIfSettled()
+ {
+ $p = new RejectedPromise(null);
+ $this->assertTrue(P\is_settled($p));
+ $p = new Promise();
+ $this->assertFalse(P\is_settled($p));
+ }
+
+ public function testReturnsTrampoline()
+ {
+ $this->assertInstanceOf('GuzzleHttp\Promise\TaskQueue', P\queue());
+ $this->assertSame(P\queue(), P\queue());
+ }
+
+ public function testCanScheduleThunk()
+ {
+ $tramp = P\queue();
+ $promise = P\task(function () { return 'Hi!'; });
+ $c = null;
+ $promise->then(function ($v) use (&$c) { $c = $v; });
+ $this->assertNull($c);
+ $tramp->run();
+ $this->assertEquals('Hi!', $c);
+ }
+
+ public function testCanScheduleThunkWithRejection()
+ {
+ $tramp = P\queue();
+ $promise = P\task(function () { throw new \Exception('Hi!'); });
+ $c = null;
+ $promise->otherwise(function ($v) use (&$c) { $c = $v; });
+ $this->assertNull($c);
+ $tramp->run();
+ $this->assertEquals('Hi!', $c->getMessage());
+ }
+
+ public function testCanScheduleThunkWithWait()
+ {
+ $tramp = P\queue();
+ $promise = P\task(function () { return 'a'; });
+ $this->assertEquals('a', $promise->wait());
+ $tramp->run();
+ }
+
+ public function testYieldsFromCoroutine()
+ {
+ $promise = P\coroutine(function () {
+ $value = (yield new P\FulfilledPromise('a'));
+ yield $value . 'b';
+ });
+ $promise->then(function ($value) use (&$result) { $result = $value; });
+ P\queue()->run();
+ $this->assertEquals('ab', $result);
+ }
+
+ public function testCanCatchExceptionsInCoroutine()
+ {
+ $promise = P\coroutine(function () {
+ try {
+ yield new P\RejectedPromise('a');
+ $this->fail('Should have thrown into the coroutine!');
+ } catch (P\RejectionException $e) {
+ $value = (yield new P\FulfilledPromise($e->getReason()));
+ yield $value . 'b';
+ }
+ });
+ $promise->then(function ($value) use (&$result) { $result = $value; });
+ P\queue()->run();
+ $this->assertEquals(P\PromiseInterface::FULFILLED, $promise->getState());
+ $this->assertEquals('ab', $result);
+ }
+
+ public function testRejectsParentExceptionWhenException()
+ {
+ $promise = P\coroutine(function () {
+ yield new P\FulfilledPromise(0);
+ throw new \Exception('a');
+ });
+ $promise->then(
+ function () { $this->fail(); },
+ function ($reason) use (&$result) { $result = $reason; }
+ );
+ P\queue()->run();
+ $this->assertInstanceOf('Exception', $result);
+ $this->assertEquals('a', $result->getMessage());
+ }
+
+ public function testCanRejectFromRejectionCallback()
+ {
+ $promise = P\coroutine(function () {
+ yield new P\FulfilledPromise(0);
+ yield new P\RejectedPromise('no!');
+ });
+ $promise->then(
+ function () { $this->fail(); },
+ function ($reason) use (&$result) { $result = $reason; }
+ );
+ P\queue()->run();
+ $this->assertInstanceOf('GuzzleHttp\Promise\RejectionException', $result);
+ $this->assertEquals('no!', $result->getReason());
+ }
+
+ public function testCanAsyncReject()
+ {
+ $rej = new P\Promise();
+ $promise = P\coroutine(function () use ($rej) {
+ yield new P\FulfilledPromise(0);
+ yield $rej;
+ });
+ $promise->then(
+ function () { $this->fail(); },
+ function ($reason) use (&$result) { $result = $reason; }
+ );
+ $rej->reject('no!');
+ P\queue()->run();
+ $this->assertInstanceOf('GuzzleHttp\Promise\RejectionException', $result);
+ $this->assertEquals('no!', $result->getReason());
+ }
+
+ public function testCanCatchAndThrowOtherException()
+ {
+ $promise = P\coroutine(function () {
+ try {
+ yield new P\RejectedPromise('a');
+ $this->fail('Should have thrown into the coroutine!');
+ } catch (P\RejectionException $e) {
+ throw new \Exception('foo');
+ }
+ });
+ $promise->otherwise(function ($value) use (&$result) { $result = $value; });
+ P\queue()->run();
+ $this->assertEquals(P\PromiseInterface::REJECTED, $promise->getState());
+ $this->assertContains('foo', $result->getMessage());
+ }
+
+ public function testCanCatchAndYieldOtherException()
+ {
+ $promise = P\coroutine(function () {
+ try {
+ yield new P\RejectedPromise('a');
+ $this->fail('Should have thrown into the coroutine!');
+ } catch (P\RejectionException $e) {
+ yield new P\RejectedPromise('foo');
+ }
+ });
+ $promise->otherwise(function ($value) use (&$result) { $result = $value; });
+ P\queue()->run();
+ $this->assertEquals(P\PromiseInterface::REJECTED, $promise->getState());
+ $this->assertContains('foo', $result->getMessage());
+ }
+
+ public function createLotsOfSynchronousPromise()
+ {
+ return P\coroutine(function () {
+ $value = 0;
+ for ($i = 0; $i < 1000; $i++) {
+ $value = (yield new P\FulfilledPromise($i));
+ }
+ yield $value;
+ });
+ }
+
+ public function testLotsOfSynchronousDoesNotBlowStack()
+ {
+ $promise = $this->createLotsOfSynchronousPromise();
+ $promise->then(function ($v) use (&$r) { $r = $v; });
+ P\queue()->run();
+ $this->assertEquals(999, $r);
+ }
+
+ public function testLotsOfSynchronousWaitDoesNotBlowStack()
+ {
+ $promise = $this->createLotsOfSynchronousPromise();
+ $promise->then(function ($v) use (&$r) { $r = $v; });
+ $this->assertEquals(999, $promise->wait());
+ $this->assertEquals(999, $r);
+ }
+
+ private function createLotsOfFlappingPromise()
+ {
+ return P\coroutine(function () {
+ $value = 0;
+ for ($i = 0; $i < 1000; $i++) {
+ try {
+ if ($i % 2) {
+ $value = (yield new P\FulfilledPromise($i));
+ } else {
+ $value = (yield new P\RejectedPromise($i));
+ }
+ } catch (\Exception $e) {
+ $value = (yield new P\FulfilledPromise($i));
+ }
+ }
+ yield $value;
+ });
+ }
+
+ public function testLotsOfTryCatchingDoesNotBlowStack()
+ {
+ $promise = $this->createLotsOfFlappingPromise();
+ $promise->then(function ($v) use (&$r) { $r = $v; });
+ P\queue()->run();
+ $this->assertEquals(999, $r);
+ }
+
+ public function testLotsOfTryCatchingWaitingDoesNotBlowStack()
+ {
+ $promise = $this->createLotsOfFlappingPromise();
+ $promise->then(function ($v) use (&$r) { $r = $v; });
+ $this->assertEquals(999, $promise->wait());
+ $this->assertEquals(999, $r);
+ }
+
+ public function testAsyncPromisesWithCorrectlyYieldedValues()
+ {
+ $promises = [
+ new P\Promise(),
+ new P\Promise(),
+ new P\Promise()
+ ];
+
+ $promise = P\coroutine(function () use ($promises) {
+ $value = null;
+ $this->assertEquals('skip', (yield new P\FulfilledPromise('skip')));
+ foreach ($promises as $idx => $p) {
+ $value = (yield $p);
+ $this->assertEquals($value, $idx);
+ $this->assertEquals('skip', (yield new P\FulfilledPromise('skip')));
+ }
+ $this->assertEquals('skip', (yield new P\FulfilledPromise('skip')));
+ yield $value;
+ });
+
+ $promises[0]->resolve(0);
+ $promises[1]->resolve(1);
+ $promises[2]->resolve(2);
+
+ $promise->then(function ($v) use (&$r) { $r = $v; });
+ P\queue()->run();
+ $this->assertEquals(2, $r);
+ }
+
+ public function testYieldFinalWaitablePromise()
+ {
+ $p1 = new P\Promise(function () use (&$p1) {
+ $p1->resolve('skip me');
+ });
+ $p2 = new P\Promise(function () use (&$p2) {
+ $p2->resolve('hello!');
+ });
+ $co = P\coroutine(function() use ($p1, $p2) {
+ yield $p1;
+ yield $p2;
+ });
+ P\queue()->run();
+ $this->assertEquals('hello!', $co->wait());
+ }
+
+ public function testCanYieldFinalPendingPromise()
+ {
+ $p1 = new P\Promise();
+ $p2 = new P\Promise();
+ $co = P\coroutine(function() use ($p1, $p2) {
+ yield $p1;
+ yield $p2;
+ });
+ $p1->resolve('a');
+ $p2->resolve('b');
+ $co->then(function ($value) use (&$result) { $result = $value; });
+ P\queue()->run();
+ $this->assertEquals('b', $result);
+ }
+
+ public function testCanNestYieldsAndFailures()
+ {
+ $p1 = new P\Promise();
+ $p2 = new P\Promise();
+ $p3 = new P\Promise();
+ $p4 = new P\Promise();
+ $p5 = new P\Promise();
+ $co = P\coroutine(function() use ($p1, $p2, $p3, $p4, $p5) {
+ try {
+ yield $p1;
+ } catch (\Exception $e) {
+ yield $p2;
+ try {
+ yield $p3;
+ yield $p4;
+ } catch (\Exception $e) {
+ yield $p5;
+ }
+ }
+ });
+ $p1->reject('a');
+ $p2->resolve('b');
+ $p3->resolve('c');
+ $p4->reject('d');
+ $p5->resolve('e');
+ $co->then(function ($value) use (&$result) { $result = $value; });
+ P\queue()->run();
+ $this->assertEquals('e', $result);
+ }
+
+ public function testCanYieldErrorsAndSuccessesWithoutRecursion()
+ {
+ $promises = [];
+ for ($i = 0; $i < 20; $i++) {
+ $promises[] = new P\Promise();
+ }
+
+ $co = P\coroutine(function() use ($promises) {
+ for ($i = 0; $i < 20; $i += 4) {
+ try {
+ yield $promises[$i];
+ yield $promises[$i + 1];
+ } catch (\Exception $e) {
+ yield $promises[$i + 2];
+ yield $promises[$i + 3];
+ }
+ }
+ });
+
+ for ($i = 0; $i < 20; $i += 4) {
+ $promises[$i]->resolve($i);
+ $promises[$i + 1]->reject($i + 1);
+ $promises[$i + 2]->resolve($i + 2);
+ $promises[$i + 3]->resolve($i + 3);
+ }
+
+ $co->then(function ($value) use (&$result) { $result = $value; });
+ P\queue()->run();
+ $this->assertEquals('19', $result);
+ }
+
+ public function testCanWaitOnPromiseAfterFulfilled()
+ {
+ $f = function () {
+ static $i = 0;
+ $i++;
+ return $p = new P\Promise(function () use (&$p, $i) {
+ $p->resolve($i . '-bar');
+ });
+ };
+
+ $promises = [];
+ for ($i = 0; $i < 20; $i++) {
+ $promises[] = $f();
+ }
+
+ $p = P\coroutine(function () use ($promises) {
+ yield new P\FulfilledPromise('foo!');
+ foreach ($promises as $promise) {
+ yield $promise;
+ }
+ });
+
+ $this->assertEquals('20-bar', $p->wait());
+ }
+
+ public function testCanWaitOnErroredPromises()
+ {
+ $p1 = new P\Promise(function () use (&$p1) { $p1->reject('a'); });
+ $p2 = new P\Promise(function () use (&$p2) { $p2->resolve('b'); });
+ $p3 = new P\Promise(function () use (&$p3) { $p3->resolve('c'); });
+ $p4 = new P\Promise(function () use (&$p4) { $p4->reject('d'); });
+ $p5 = new P\Promise(function () use (&$p5) { $p5->resolve('e'); });
+ $p6 = new P\Promise(function () use (&$p6) { $p6->reject('f'); });
+
+ $co = P\coroutine(function() use ($p1, $p2, $p3, $p4, $p5, $p6) {
+ try {
+ yield $p1;
+ } catch (\Exception $e) {
+ yield $p2;
+ try {
+ yield $p3;
+ yield $p4;
+ } catch (\Exception $e) {
+ yield $p5;
+ yield $p6;
+ }
+ }
+ });
+
+ $res = P\inspect($co);
+ $this->assertEquals('f', $res['reason']);
+ }
+
+ public function testCoroutineOtherwiseIntegrationTest()
+ {
+ $a = new P\Promise();
+ $b = new P\Promise();
+ $promise = P\coroutine(function () use ($a, $b) {
+ // Execute the pool of commands concurrently, and process errors.
+ yield $a;
+ yield $b;
+ })->otherwise(function (\Exception $e) {
+ // Throw errors from the operations as a specific Multipart error.
+ throw new \OutOfBoundsException('a', 0, $e);
+ });
+ $a->resolve('a');
+ $b->reject('b');
+ $reason = P\inspect($promise)['reason'];
+ $this->assertInstanceOf('OutOfBoundsException', $reason);
+ $this->assertInstanceOf('GuzzleHttp\Promise\RejectionException', $reason->getPrevious());
+ }
+}