Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Promise.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 8.73 KiB


001  <?php
002   
003  namespace GuzzleHttp\Promise;
004   
005  /**
006   * Promises/A+ implementation that avoids recursion when possible.
007   *
008   * @link https://promisesaplus.com/
009   */
010  class Promise implements PromiseInterface
011  {
012      private $state = self::PENDING;
013      private $result;
014      private $cancelFn;
015      private $waitFn;
016      private $waitList;
017      private $handlers = [];
018   
019      /**
020       * @param callable $waitFn   Fn that when invoked resolves the promise.
021       * @param callable $cancelFn Fn that when invoked cancels the promise.
022       */
023      public function __construct(
024          callable $waitFn = null,
025          callable $cancelFn = null
026      ) {
027          $this->waitFn = $waitFn;
028          $this->cancelFn = $cancelFn;
029      }
030   
031      public function then(
032          callable $onFulfilled = null,
033          callable $onRejected = null
034      ) {
035          if ($this->state === self::PENDING) {
036              $p = new Promise(null, [$this, 'cancel']);
037              $this->handlers[] = [$p, $onFulfilled, $onRejected];
038              $p->waitList = $this->waitList;
039              $p->waitList[] = $this;
040              return $p;
041          }
042   
043          // Return a fulfilled promise and immediately invoke any callbacks.
044          if ($this->state === self::FULFILLED) {
045              $promise = Create::promiseFor($this->result);
046              return $onFulfilled ? $promise->then($onFulfilled) : $promise;
047          }
048   
049          // It's either cancelled or rejected, so return a rejected promise
050          // and immediately invoke any callbacks.
051          $rejection = Create::rejectionFor($this->result);
052          return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
053      }
054   
055      public function otherwise(callable $onRejected)
056      {
057          return $this->then(null, $onRejected);
058      }
059   
060      public function wait($unwrap = true)
061      {
062          $this->waitIfPending();
063   
064          if ($this->result instanceof PromiseInterface) {
065              return $this->result->wait($unwrap);
066          }
067          if ($unwrap) {
068              if ($this->state === self::FULFILLED) {
069                  return $this->result;
070              }
071              // It's rejected so "unwrap" and throw an exception.
072              throw Create::exceptionFor($this->result);
073          }
074      }
075   
076      public function getState()
077      {
078          return $this->state;
079      }
080   
081      public function cancel()
082      {
083          if ($this->state !== self::PENDING) {
084              return;
085          }
086   
087          $this->waitFn = $this->waitList = null;
088   
089          if ($this->cancelFn) {
090              $fn = $this->cancelFn;
091              $this->cancelFn = null;
092              try {
093                  $fn();
094              } catch (\Throwable $e) {
095                  $this->reject($e);
096              } catch (\Exception $e) {
097                  $this->reject($e);
098              }
099          }
100   
101          // Reject the promise only if it wasn't rejected in a then callback.
102          /** @psalm-suppress RedundantCondition */
103          if ($this->state === self::PENDING) {
104              $this->reject(new CancellationException('Promise has been cancelled'));
105          }
106      }
107   
108      public function resolve($value)
109      {
110          $this->settle(self::FULFILLED, $value);
111      }
112   
113      public function reject($reason)
114      {
115          $this->settle(self::REJECTED, $reason);
116      }
117   
118      private function settle($state, $value)
119      {
120          if ($this->state !== self::PENDING) {
121              // Ignore calls with the same resolution.
122              if ($state === $this->state && $value === $this->result) {
123                  return;
124              }
125              throw $this->state === $state
126                  ? new \LogicException("The promise is already {$state}.")
127                  : new \LogicException("Cannot change a {$this->state} promise to {$state}");
128          }
129   
130          if ($value === $this) {
131              throw new \LogicException('Cannot fulfill or reject a promise with itself');
132          }
133   
134          // Clear out the state of the promise but stash the handlers.
135          $this->state = $state;
136          $this->result = $value;
137          $handlers = $this->handlers;
138          $this->handlers = null;
139          $this->waitList = $this->waitFn = null;
140          $this->cancelFn = null;
141   
142          if (!$handlers) {
143              return;
144          }
145   
146          // If the value was not a settled promise or a thenable, then resolve
147          // it in the task queue using the correct ID.
148          if (!is_object($value) || !method_exists($value, 'then')) {
149              $id = $state === self::FULFILLED ? 1 : 2;
150              // It's a success, so resolve the handlers in the queue.
151              Utils::queue()->add(static function () use ($id, $value, $handlers) {
152                  foreach ($handlers as $handler) {
153                      self::callHandler($id, $value, $handler);
154                  }
155              });
156          } elseif ($value instanceof Promise && Is::pending($value)) {
157              // We can just merge our handlers onto the next promise.
158              $value->handlers = array_merge($value->handlers, $handlers);
159          } else {
160              // Resolve the handlers when the forwarded promise is resolved.
161              $value->then(
162                  static function ($value) use ($handlers) {
163                      foreach ($handlers as $handler) {
164                          self::callHandler(1, $value, $handler);
165                      }
166                  },
167                  static function ($reason) use ($handlers) {
168                      foreach ($handlers as $handler) {
169                          self::callHandler(2, $reason, $handler);
170                      }
171                  }
172              );
173          }
174      }
175   
176      /**
177       * Call a stack of handlers using a specific callback index and value.
178       *
179       * @param int   $index   1 (resolve) or 2 (reject).
180       * @param mixed $value   Value to pass to the callback.
181       * @param array $handler Array of handler data (promise and callbacks).
182       */
183      private static function callHandler($index, $value, array $handler)
184      {
185          /** @var PromiseInterface $promise */
186          $promise = $handler[0];
187   
188          // The promise may have been cancelled or resolved before placing
189          // this thunk in the queue.
190          if (Is::settled($promise)) {
191              return;
192          }
193   
194          try {
195              if (isset($handler[$index])) {
196                  /*
197                   * If $f throws an exception, then $handler will be in the exception
198                   * stack trace. Since $handler contains a reference to the callable
199                   * itself we get a circular reference. We clear the $handler
200                   * here to avoid that memory leak.
201                   */
202                  $f = $handler[$index];
203                  unset($handler);
204                  $promise->resolve($f($value));
205              } elseif ($index === 1) {
206                  // Forward resolution values as-is.
207                  $promise->resolve($value);
208              } else {
209                  // Forward rejections down the chain.
210                  $promise->reject($value);
211              }
212          } catch (\Throwable $reason) {
213              $promise->reject($reason);
214          } catch (\Exception $reason) {
215              $promise->reject($reason);
216          }
217      }
218   
219      private function waitIfPending()
220      {
221          if ($this->state !== self::PENDING) {
222              return;
223          } elseif ($this->waitFn) {
224              $this->invokeWaitFn();
225          } elseif ($this->waitList) {
226              $this->invokeWaitList();
227          } else {
228              // If there's no wait function, then reject the promise.
229              $this->reject('Cannot wait on a promise that has '
230                  . 'no internal wait function. You must provide a wait '
231                  . 'function when constructing the promise to be able to '
232                  . 'wait on a promise.');
233          }
234   
235          Utils::queue()->run();
236   
237          /** @psalm-suppress RedundantCondition */
238          if ($this->state === self::PENDING) {
239              $this->reject('Invoking the wait callback did not resolve the promise');
240          }
241      }
242   
243      private function invokeWaitFn()
244      {
245          try {
246              $wfn = $this->waitFn;
247              $this->waitFn = null;
248              $wfn(true);
249          } catch (\Exception $reason) {
250              if ($this->state === self::PENDING) {
251                  // The promise has not been resolved yet, so reject the promise
252                  // with the exception.
253                  $this->reject($reason);
254              } else {
255                  // The promise was already resolved, so there's a problem in
256                  // the application.
257                  throw $reason;
258              }
259          }
260      }
261   
262      private function invokeWaitList()
263      {
264          $waitList = $this->waitList;
265          $this->waitList = null;
266   
267          foreach ($waitList as $result) {
268              do {
269                  $result->waitIfPending();
270                  $result = $result->result;
271              } while ($result instanceof Promise);
272   
273              if ($result instanceof PromiseInterface) {
274                  $result->wait(false);
275              }
276          }
277      }
278  }
279