PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.7
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.7
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / vendor_prefixed / guzzlehttp / promises / src / Promise.php

Promise.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.7, at vendor_prefixed/guzzlehttp/promises/src/Promise.php

238 lines 9.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace YoastSEO_Vendor\GuzzleHttp\Promise;
4
5 /**
6 * Promises/A+ implementation that avoids recursion when possible.
7 *
8 * @link https://promisesaplus.com/
9 */
10 class Promise implements \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface
11 {
12 private $state = self::PENDING;
13 private $result;
14 private $cancelFn;
15 private $waitFn;
16 private $waitList;
17 private $handlers = [];
18 /**
19 * @param callable $waitFn Fn that when invoked resolves the promise.
20 * @param callable $cancelFn Fn that when invoked cancels the promise.
21 */
22 public function __construct(callable $waitFn = null, callable $cancelFn = null)
23 {
24 $this->waitFn = $waitFn;
25 $this->cancelFn = $cancelFn;
26 }
27 public function then(callable $onFulfilled = null, callable $onRejected = null)
28 {
29 if ($this->state === self::PENDING) {
30 $p = new \YoastSEO_Vendor\GuzzleHttp\Promise\Promise(null, [$this, 'cancel']);
31 $this->handlers[] = [$p, $onFulfilled, $onRejected];
32 $p->waitList = $this->waitList;
33 $p->waitList[] = $this;
34 return $p;
35 }
36 // Return a fulfilled promise and immediately invoke any callbacks.
37 if ($this->state === self::FULFILLED) {
38 $promise = \YoastSEO_Vendor\GuzzleHttp\Promise\Create::promiseFor($this->result);
39 return $onFulfilled ? $promise->then($onFulfilled) : $promise;
40 }
41 // It's either cancelled or rejected, so return a rejected promise
42 // and immediately invoke any callbacks.
43 $rejection = \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($this->result);
44 return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
45 }
46 public function otherwise(callable $onRejected)
47 {
48 return $this->then(null, $onRejected);
49 }
50 public function wait($unwrap = \true)
51 {
52 $this->waitIfPending();
53 if ($this->result instanceof \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface) {
54 return $this->result->wait($unwrap);
55 }
56 if ($unwrap) {
57 if ($this->state === self::FULFILLED) {
58 return $this->result;
59 }
60 // It's rejected so "unwrap" and throw an exception.
61 throw \YoastSEO_Vendor\GuzzleHttp\Promise\Create::exceptionFor($this->result);
62 }
63 }
64 public function getState()
65 {
66 return $this->state;
67 }
68 public function cancel()
69 {
70 if ($this->state !== self::PENDING) {
71 return;
72 }
73 $this->waitFn = $this->waitList = null;
74 if ($this->cancelFn) {
75 $fn = $this->cancelFn;
76 $this->cancelFn = null;
77 try {
78 $fn();
79 } catch (\Throwable $e) {
80 $this->reject($e);
81 } catch (\Exception $e) {
82 $this->reject($e);
83 }
84 }
85 // Reject the promise only if it wasn't rejected in a then callback.
86 /** @psalm-suppress RedundantCondition */
87 if ($this->state === self::PENDING) {
88 $this->reject(new \YoastSEO_Vendor\GuzzleHttp\Promise\CancellationException('Promise has been cancelled'));
89 }
90 }
91 public function resolve($value)
92 {
93 $this->settle(self::FULFILLED, $value);
94 }
95 public function reject($reason)
96 {
97 $this->settle(self::REJECTED, $reason);
98 }
99 private function settle($state, $value)
100 {
101 if ($this->state !== self::PENDING) {
102 // Ignore calls with the same resolution.
103 if ($state === $this->state && $value === $this->result) {
104 return;
105 }
106 throw $this->state === $state ? new \LogicException("The promise is already {$state}.") : new \LogicException("Cannot change a {$this->state} promise to {$state}");
107 }
108 if ($value === $this) {
109 throw new \LogicException('Cannot fulfill or reject a promise with itself');
110 }
111 // Clear out the state of the promise but stash the handlers.
112 $this->state = $state;
113 $this->result = $value;
114 $handlers = $this->handlers;
115 $this->handlers = null;
116 $this->waitList = $this->waitFn = null;
117 $this->cancelFn = null;
118 if (!$handlers) {
119 return;
120 }
121 // If the value was not a settled promise or a thenable, then resolve
122 // it in the task queue using the correct ID.
123 if (!\is_object($value) || !\method_exists($value, 'then')) {
124 $id = $state === self::FULFILLED ? 1 : 2;
125 // It's a success, so resolve the handlers in the queue.
126 \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue()->add(static function () use($id, $value, $handlers) {
127 foreach ($handlers as $handler) {
128 self::callHandler($id, $value, $handler);
129 }
130 });
131 } elseif ($value instanceof \YoastSEO_Vendor\GuzzleHttp\Promise\Promise && \YoastSEO_Vendor\GuzzleHttp\Promise\Is::pending($value)) {
132 // We can just merge our handlers onto the next promise.
133 $value->handlers = \array_merge($value->handlers, $handlers);
134 } else {
135 // Resolve the handlers when the forwarded promise is resolved.
136 $value->then(static function ($value) use($handlers) {
137 foreach ($handlers as $handler) {
138 self::callHandler(1, $value, $handler);
139 }
140 }, static function ($reason) use($handlers) {
141 foreach ($handlers as $handler) {
142 self::callHandler(2, $reason, $handler);
143 }
144 });
145 }
146 }
147 /**
148 * Call a stack of handlers using a specific callback index and value.
149 *
150 * @param int $index 1 (resolve) or 2 (reject).
151 * @param mixed $value Value to pass to the callback.
152 * @param array $handler Array of handler data (promise and callbacks).
153 */
154 private static function callHandler($index, $value, array $handler)
155 {
156 /** @var PromiseInterface $promise */
157 $promise = $handler[0];
158 // The promise may have been cancelled or resolved before placing
159 // this thunk in the queue.
160 if (\YoastSEO_Vendor\GuzzleHttp\Promise\Is::settled($promise)) {
161 return;
162 }
163 try {
164 if (isset($handler[$index])) {
165 /*
166 * If $f throws an exception, then $handler will be in the exception
167 * stack trace. Since $handler contains a reference to the callable
168 * itself we get a circular reference. We clear the $handler
169 * here to avoid that memory leak.
170 */
171 $f = $handler[$index];
172 unset($handler);
173 $promise->resolve($f($value));
174 } elseif ($index === 1) {
175 // Forward resolution values as-is.
176 $promise->resolve($value);
177 } else {
178 // Forward rejections down the chain.
179 $promise->reject($value);
180 }
181 } catch (\Throwable $reason) {
182 $promise->reject($reason);
183 } catch (\Exception $reason) {
184 $promise->reject($reason);
185 }
186 }
187 private function waitIfPending()
188 {
189 if ($this->state !== self::PENDING) {
190 return;
191 } elseif ($this->waitFn) {
192 $this->invokeWaitFn();
193 } elseif ($this->waitList) {
194 $this->invokeWaitList();
195 } else {
196 // If there's no wait function, then reject the promise.
197 $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.');
198 }
199 \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue()->run();
200 /** @psalm-suppress RedundantCondition */
201 if ($this->state === self::PENDING) {
202 $this->reject('Invoking the wait callback did not resolve the promise');
203 }
204 }
205 private function invokeWaitFn()
206 {
207 try {
208 $wfn = $this->waitFn;
209 $this->waitFn = null;
210 $wfn(\true);
211 } catch (\Exception $reason) {
212 if ($this->state === self::PENDING) {
213 // The promise has not been resolved yet, so reject the promise
214 // with the exception.
215 $this->reject($reason);
216 } else {
217 // The promise was already resolved, so there's a problem in
218 // the application.
219 throw $reason;
220 }
221 }
222 }
223 private function invokeWaitList()
224 {
225 $waitList = $this->waitList;
226 $this->waitList = null;
227 foreach ($waitList as $result) {
228 do {
229 $result->waitIfPending();
230 $result = $result->result;
231 } while ($result instanceof \YoastSEO_Vendor\GuzzleHttp\Promise\Promise);
232 if ($result instanceof \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface) {
233 $result->wait(\false);
234 }
235 }
236 }
237 }
238