Security update to Drupal 8.4.6
[yaffs-website] / vendor / guzzlehttp / promises / src / Coroutine.php
1 <?php
2 namespace GuzzleHttp\Promise;
3
4 use Exception;
5 use Generator;
6 use Throwable;
7
8 /**
9  * Creates a promise that is resolved using a generator that yields values or
10  * promises (somewhat similar to C#'s async keyword).
11  *
12  * When called, the coroutine function will start an instance of the generator
13  * and returns a promise that is fulfilled with its final yielded value.
14  *
15  * Control is returned back to the generator when the yielded promise settles.
16  * This can lead to less verbose code when doing lots of sequential async calls
17  * with minimal processing in between.
18  *
19  *     use GuzzleHttp\Promise;
20  *
21  *     function createPromise($value) {
22  *         return new Promise\FulfilledPromise($value);
23  *     }
24  *
25  *     $promise = Promise\coroutine(function () {
26  *         $value = (yield createPromise('a'));
27  *         try {
28  *             $value = (yield createPromise($value . 'b'));
29  *         } catch (\Exception $e) {
30  *             // The promise was rejected.
31  *         }
32  *         yield $value . 'c';
33  *     });
34  *
35  *     // Outputs "abc"
36  *     $promise->then(function ($v) { echo $v; });
37  *
38  * @param callable $generatorFn Generator function to wrap into a promise.
39  *
40  * @return Promise
41  * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
42  */
43 final class Coroutine implements PromiseInterface
44 {
45     /**
46      * @var PromiseInterface|null
47      */
48     private $currentPromise;
49
50     /**
51      * @var Generator
52      */
53     private $generator;
54
55     /**
56      * @var Promise
57      */
58     private $result;
59
60     public function __construct(callable $generatorFn)
61     {
62         $this->generator = $generatorFn();
63         $this->result = new Promise(function () {
64             while (isset($this->currentPromise)) {
65                 $this->currentPromise->wait();
66             }
67         });
68         $this->nextCoroutine($this->generator->current());
69     }
70
71     public function then(
72         callable $onFulfilled = null,
73         callable $onRejected = null
74     ) {
75         return $this->result->then($onFulfilled, $onRejected);
76     }
77
78     public function otherwise(callable $onRejected)
79     {
80         return $this->result->otherwise($onRejected);
81     }
82
83     public function wait($unwrap = true)
84     {
85         return $this->result->wait($unwrap);
86     }
87
88     public function getState()
89     {
90         return $this->result->getState();
91     }
92
93     public function resolve($value)
94     {
95         $this->result->resolve($value);
96     }
97
98     public function reject($reason)
99     {
100         $this->result->reject($reason);
101     }
102
103     public function cancel()
104     {
105         $this->currentPromise->cancel();
106         $this->result->cancel();
107     }
108
109     private function nextCoroutine($yielded)
110     {
111         $this->currentPromise = promise_for($yielded)
112             ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
113     }
114
115     /**
116      * @internal
117      */
118     public function _handleSuccess($value)
119     {
120         unset($this->currentPromise);
121         try {
122             $next = $this->generator->send($value);
123             if ($this->generator->valid()) {
124                 $this->nextCoroutine($next);
125             } else {
126                 $this->result->resolve($value);
127             }
128         } catch (Exception $exception) {
129             $this->result->reject($exception);
130         } catch (Throwable $throwable) {
131             $this->result->reject($throwable);
132         }
133     }
134
135     /**
136      * @internal
137      */
138     public function _handleFailure($reason)
139     {
140         unset($this->currentPromise);
141         try {
142             $nextYield = $this->generator->throw(exception_for($reason));
143             // The throw was caught, so keep iterating on the coroutine
144             $this->nextCoroutine($nextYield);
145         } catch (Exception $exception) {
146             $this->result->reject($exception);
147         } catch (Throwable $throwable) {
148             $this->result->reject($throwable);
149         }
150     }
151 }