PluginProbe ʕ •ᴥ•ʔ
Check & Log Email – Easy Email Testing & Mail logging / 2.0.15
Check & Log Email – Easy Email Testing & Mail logging v2.0.15
2.0.15 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 2.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.13.1 2.0.13.2 2.0.14 2.0.2 2.0.3 2.0.4 2.0.5 2.0.5.1 2.0.6 2.0.7 2.0.8 2.0.9 trunk 0.5.7 0.6.0 0.6.1 0.6.2 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.12.1 1.0.13 1.0.13.1 1.0.2 1.0.3
check-email / vendor / guzzlehttp / promises / src / Promise.php
check-email / vendor / guzzlehttp / promises / src Last commit date
AggregateException.php 1 week ago CancellationException.php 1 week ago Coroutine.php 1 week ago Create.php 1 week ago Each.php 1 week ago EachPromise.php 1 week ago FulfilledPromise.php 1 week ago Is.php 1 week ago Promise.php 1 week ago PromiseInterface.php 1 week ago PromisorInterface.php 1 week ago RejectedPromise.php 1 week ago RejectionException.php 1 week ago TaskQueue.php 1 week ago TaskQueueInterface.php 1 week ago Utils.php 1 week ago
Promise.php
282 lines
1 <?php
2
3 declare(strict_types=1);
4
5 namespace GuzzleHttp\Promise;
6
7 /**
8 * Promises/A+ implementation that avoids recursion when possible.
9 *
10 * @see https://promisesaplus.com/
11 *
12 * @final
13 */
14 class Promise implements PromiseInterface
15 {
16 private $state = self::PENDING;
17 private $result;
18 private $cancelFn;
19 private $waitFn;
20 private $waitList;
21 private $handlers = [];
22
23 /**
24 * @param callable $waitFn Fn that when invoked resolves the promise.
25 * @param callable $cancelFn Fn that when invoked cancels the promise.
26 */
27 public function __construct(
28 ?callable $waitFn = null,
29 ?callable $cancelFn = null
30 ) {
31 $this->waitFn = $waitFn;
32 $this->cancelFn = $cancelFn;
33 }
34
35 public function then(
36 ?callable $onFulfilled = null,
37 ?callable $onRejected = null
38 ): PromiseInterface {
39 if ($this->state === self::PENDING) {
40 $p = new Promise(null, [$this, 'cancel']);
41 $this->handlers[] = [$p, $onFulfilled, $onRejected];
42 $p->waitList = $this->waitList;
43 $p->waitList[] = $this;
44
45 return $p;
46 }
47
48 // Return a fulfilled promise and immediately invoke any callbacks.
49 if ($this->state === self::FULFILLED) {
50 $promise = Create::promiseFor($this->result);
51
52 return $onFulfilled ? $promise->then($onFulfilled) : $promise;
53 }
54
55 // It's either cancelled or rejected, so return a rejected promise
56 // and immediately invoke any callbacks.
57 $rejection = Create::rejectionFor($this->result);
58
59 return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
60 }
61
62 public function otherwise(callable $onRejected): PromiseInterface
63 {
64 return $this->then(null, $onRejected);
65 }
66
67 public function wait(bool $unwrap = true)
68 {
69 $this->waitIfPending();
70
71 if ($this->result instanceof PromiseInterface) {
72 return $this->result->wait($unwrap);
73 }
74 if ($unwrap) {
75 if ($this->state === self::FULFILLED) {
76 return $this->result;
77 }
78 // It's rejected so "unwrap" and throw an exception.
79 throw Create::exceptionFor($this->result);
80 }
81 }
82
83 public function getState(): string
84 {
85 return $this->state;
86 }
87
88 public function cancel(): void
89 {
90 if ($this->state !== self::PENDING) {
91 return;
92 }
93
94 $this->waitFn = $this->waitList = null;
95
96 if ($this->cancelFn) {
97 $fn = $this->cancelFn;
98 $this->cancelFn = null;
99 try {
100 $fn();
101 } catch (\Throwable $e) {
102 $this->reject($e);
103 }
104 }
105
106 // Reject the promise only if it wasn't rejected in a then callback.
107 /** @psalm-suppress RedundantCondition */
108 if ($this->state === self::PENDING) {
109 $this->reject(new CancellationException('Promise has been cancelled'));
110 }
111 }
112
113 public function resolve($value): void
114 {
115 $this->settle(self::FULFILLED, $value);
116 }
117
118 public function reject($reason): void
119 {
120 $this->settle(self::REJECTED, $reason);
121 }
122
123 private function settle(string $state, $value): void
124 {
125 if ($this->state !== self::PENDING) {
126 // Ignore calls with the same resolution.
127 if ($state === $this->state && $value === $this->result) {
128 return;
129 }
130 throw $this->state === $state
131 ? new \LogicException("The promise is already {$state}.")
132 : new \LogicException("Cannot change a {$this->state} promise to {$state}");
133 }
134
135 if ($value === $this) {
136 throw new \LogicException('Cannot fulfill or reject a promise with itself');
137 }
138
139 // Clear out the state of the promise but stash the handlers.
140 $this->state = $state;
141 $this->result = $value;
142 $handlers = $this->handlers;
143 $this->handlers = null;
144 $this->waitList = $this->waitFn = null;
145 $this->cancelFn = null;
146
147 if (!$handlers) {
148 return;
149 }
150
151 // If the value was not a settled promise or a thenable, then resolve
152 // it in the task queue using the correct ID.
153 if (!is_object($value) || !method_exists($value, 'then')) {
154 $id = $state === self::FULFILLED ? 1 : 2;
155 // It's a success, so resolve the handlers in the queue.
156 Utils::queue()->add(static function () use ($id, $value, $handlers): void {
157 foreach ($handlers as $handler) {
158 self::callHandler($id, $value, $handler);
159 }
160 });
161 } elseif ($value instanceof Promise && Is::pending($value)) {
162 // We can just merge our handlers onto the next promise.
163 $value->handlers = array_merge($value->handlers, $handlers);
164 } else {
165 // Resolve the handlers when the forwarded promise is resolved.
166 $value->then(
167 static function ($value) use ($handlers): void {
168 foreach ($handlers as $handler) {
169 self::callHandler(1, $value, $handler);
170 }
171 },
172 static function ($reason) use ($handlers): void {
173 foreach ($handlers as $handler) {
174 self::callHandler(2, $reason, $handler);
175 }
176 }
177 );
178 }
179 }
180
181 /**
182 * Call a stack of handlers using a specific callback index and value.
183 *
184 * @param int $index 1 (resolve) or 2 (reject).
185 * @param mixed $value Value to pass to the callback.
186 * @param array $handler Array of handler data (promise and callbacks).
187 */
188 private static function callHandler(int $index, $value, array $handler): void
189 {
190 /** @var PromiseInterface $promise */
191 $promise = $handler[0];
192
193 // The promise may have been cancelled or resolved before placing
194 // this thunk in the queue.
195 if (Is::settled($promise)) {
196 return;
197 }
198
199 try {
200 if (isset($handler[$index])) {
201 /*
202 * If $f throws an exception, then $handler will be in the exception
203 * stack trace. Since $handler contains a reference to the callable
204 * itself we get a circular reference. We clear the $handler
205 * here to avoid that memory leak.
206 */
207 $f = $handler[$index];
208 unset($handler);
209 $promise->resolve($f($value));
210 } elseif ($index === 1) {
211 // Forward resolution values as-is.
212 $promise->resolve($value);
213 } else {
214 // Forward rejections down the chain.
215 $promise->reject($value);
216 }
217 } catch (\Throwable $reason) {
218 $promise->reject($reason);
219 }
220 }
221
222 private function waitIfPending(): void
223 {
224 if ($this->state !== self::PENDING) {
225 return;
226 } elseif ($this->waitFn) {
227 $this->invokeWaitFn();
228 } elseif ($this->waitList) {
229 $this->invokeWaitList();
230 } else {
231 // If there's no wait function, then reject the promise.
232 $this->reject('Cannot wait on a promise that has '
233 .'no internal wait function. You must provide a wait '
234 .'function when constructing the promise to be able to '
235 .'wait on a promise.');
236 }
237
238 Utils::queue()->run();
239
240 /** @psalm-suppress RedundantCondition */
241 if ($this->state === self::PENDING) {
242 $this->reject('Invoking the wait callback did not resolve the promise');
243 }
244 }
245
246 private function invokeWaitFn(): void
247 {
248 try {
249 $wfn = $this->waitFn;
250 $this->waitFn = null;
251 $wfn(true);
252 } catch (\Throwable $reason) {
253 if ($this->state === self::PENDING) {
254 // The promise has not been resolved yet, so reject the promise
255 // with the exception.
256 $this->reject($reason);
257 } else {
258 // The promise was already resolved, so there's a problem in
259 // the application.
260 throw $reason;
261 }
262 }
263 }
264
265 private function invokeWaitList(): void
266 {
267 $waitList = $this->waitList;
268 $this->waitList = null;
269
270 foreach ($waitList as $result) {
271 do {
272 $result->waitIfPending();
273 $result = $result->result;
274 } while ($result instanceof Promise);
275
276 if ($result instanceof PromiseInterface) {
277 $result->wait(false);
278 }
279 }
280 }
281 }
282