GuzzlePromiseAdapter.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Http\Client;
  8. use Exception;
  9. use GuzzleHttp\Exception\RequestException;
  10. use GuzzleHttp\Promise\PromiseInterface;
  11. use LogicException;
  12. use OCP\Http\Client\IPromise;
  13. use OCP\Http\Client\IResponse;
  14. use Psr\Http\Message\ResponseInterface;
  15. use Psr\Log\LoggerInterface;
  16. /**
  17. * A wrapper around Guzzle's PromiseInterface
  18. *
  19. * @see \GuzzleHttp\Promise\PromiseInterface
  20. * @since 28.0.0
  21. */
  22. class GuzzlePromiseAdapter implements IPromise {
  23. public function __construct(
  24. protected PromiseInterface $promise,
  25. protected LoggerInterface $logger,
  26. ) {
  27. }
  28. /**
  29. * Appends fulfillment and rejection handlers to the promise, and returns
  30. * a new promise resolving to the return value of the called handler.
  31. *
  32. * @param ?callable(IResponse): void $onFulfilled Invoked when the promise fulfills. Gets an \OCP\Http\Client\IResponse passed in as argument
  33. * @param ?callable(Exception): void $onRejected Invoked when the promise is rejected. Gets an \Exception passed in as argument
  34. *
  35. * @return IPromise
  36. * @since 28.0.0
  37. */
  38. public function then(
  39. ?callable $onFulfilled = null,
  40. ?callable $onRejected = null,
  41. ): IPromise {
  42. if ($onFulfilled !== null) {
  43. $wrappedOnFulfilled = static function (ResponseInterface $response) use ($onFulfilled) {
  44. $onFulfilled(new Response($response));
  45. };
  46. } else {
  47. $wrappedOnFulfilled = null;
  48. }
  49. if ($onRejected !== null) {
  50. $wrappedOnRejected = static function (RequestException $e) use ($onRejected) {
  51. $onRejected($e);
  52. };
  53. } else {
  54. $wrappedOnRejected = null;
  55. }
  56. $this->promise->then($wrappedOnFulfilled, $wrappedOnRejected);
  57. return $this;
  58. }
  59. /**
  60. * Get the state of the promise ("pending", "rejected", or "fulfilled").
  61. *
  62. * The three states can be checked against the constants defined:
  63. * STATE_PENDING, STATE_FULFILLED, and STATE_REJECTED.
  64. *
  65. * @return IPromise::STATE_*
  66. * @since 28.0.0
  67. */
  68. public function getState(): string {
  69. $state = $this->promise->getState();
  70. if ($state === PromiseInterface::FULFILLED) {
  71. return self::STATE_FULFILLED;
  72. }
  73. if ($state === PromiseInterface::REJECTED) {
  74. return self::STATE_REJECTED;
  75. }
  76. if ($state === PromiseInterface::PENDING) {
  77. return self::STATE_PENDING;
  78. }
  79. $this->logger->error('Unexpected promise state "{state}" returned by Guzzle', [
  80. 'state' => $state,
  81. ]);
  82. return self::STATE_PENDING;
  83. }
  84. /**
  85. * Cancels the promise if possible.
  86. *
  87. * @link https://github.com/promises-aplus/cancellation-spec/issues/7
  88. * @since 28.0.0
  89. */
  90. public function cancel(): void {
  91. $this->promise->cancel();
  92. }
  93. /**
  94. * Waits until the promise completes if possible.
  95. *
  96. * Pass $unwrap as true to unwrap the result of the promise, either
  97. * returning the resolved value or throwing the rejected exception.
  98. *
  99. * If the promise cannot be waited on, then the promise will be rejected.
  100. *
  101. * @param bool $unwrap
  102. *
  103. * @return mixed
  104. *
  105. * @throws LogicException if the promise has no wait function or if the
  106. * promise does not settle after waiting.
  107. * @since 28.0.0
  108. */
  109. public function wait(bool $unwrap = true): mixed {
  110. return $this->promise->wait($unwrap);
  111. }
  112. }