PluginProbe
Media Cloud Sync / 1.3.11
Media Cloud Sync v1.3.11
1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 All 34 releases
media-cloud-sync / includes / sdk / s3 / GuzzleHttp / Promise / Promise.php

Promise.php in Media Cloud Sync 1.3.11, at includes/sdk/s3/GuzzleHttp/Promise/Promise.php

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