diff --git a/src/Appwrite/GraphQL/Promises/Adapter.php b/src/Appwrite/GraphQL/Promises/Adapter.php new file mode 100644 index 0000000000..86270f2a8b --- /dev/null +++ b/src/Appwrite/GraphQL/Promises/Adapter.php @@ -0,0 +1,88 @@ +adoptedPromise; + + return new GQLPromise($adoptedPromise->then($onFulfilled, $onRejected), $this); + } + + /** + * Create a new promise with the given resolver function. + * + * @param callable $resolver + * @return GQLPromise + */ + abstract public function create(callable $resolver): GQLPromise; + + /** + * Create a new promise that is fulfilled with the given value. + * + * @param mixed $value + * @return GQLPromise + */ + abstract public function createFulfilled(mixed $value = null): GQLPromise; + + /** + * Create a new promise that is rejected with the given reason. + * + * @param mixed $reason + * @return GQLPromise + */ + abstract public function createRejected(mixed $reason): GQLPromise; + + /** + * Create a new promise that resolves when all passed in promises resolve. + * + * @param array $promisesOrValues + * @return GQLPromise + */ + abstract public function all(array $promisesOrValues): GQLPromise; +} diff --git a/src/Appwrite/GraphQL/Promises/Adapter/Swoole.php b/src/Appwrite/GraphQL/Promises/Adapter/Swoole.php new file mode 100644 index 0000000000..caab88afa3 --- /dev/null +++ b/src/Appwrite/GraphQL/Promises/Adapter/Swoole.php @@ -0,0 +1,41 @@ +adoptedPromise; - - return new Promise($adoptedPromise->then($onFulfilled, $onRejected), $this); - } - - /** - * Create a new promise with the given resolver function. - * - * @param callable $resolver - * @return Promise - */ - public function create(callable $resolver): Promise - { - $promise = new CoroutinePromise(function ($resolve, $reject) use ($resolver) { - $resolver($resolve, $reject); - }); - - return new Promise($promise, $this); - } - - /** - * Create a new promise that is fulfilled with the given value. - * - * @param $value - * @return Promise - */ - public function createFulfilled($value = null): Promise - { - $promise = new CoroutinePromise(function ($resolve, $reject) use ($value) { - $resolve($value); - }); - - return new Promise($promise, $this); - } - - /** - * Create a new promise that is rejected with the given reason. - * - * @param $reason - * @return Promise - */ - public function createRejected($reason): Promise - { - $promise = new CoroutinePromise(function ($resolve, $reject) use ($reason) { - $reject($reason); - }); - - return new Promise($promise, $this); - } - - /** - * Create a new promise that resolves when all passed in promises resolve. - * - * @param array $promisesOrValues - * @return Promise - */ - public function all(array $promisesOrValues): Promise - { - $all = new CoroutinePromise(function (callable $resolve, callable $reject) use ($promisesOrValues) { - $ticks = count($promisesOrValues); - $firstError = null; - $channel = new Channel($ticks); - $result = []; - $key = 0; - - foreach ($promisesOrValues as $promiseOrValue) { - if (!$promiseOrValue instanceof Promise) { - $result[$key] = $promiseOrValue; - $channel->push(true); - } - $promiseOrValue->then(function ($value) use ($key, &$result, $channel) { - $result[$key] = $value; - $channel->push(true); - }, function ($error) use ($channel, &$firstError) { - $channel->push(true); - if ($firstError === null) { - $firstError = $error; - } - }); - $key++; - } - - while ($ticks--) { - $channel->pop(); - } - $channel->close(); - - if ($firstError !== null) { - $reject($firstError); - return; - } - - $resolve($result); - }); - - return new Promise($all, $this); - } -} diff --git a/src/Appwrite/GraphQL/Promises/CoroutinePromise.php b/src/Appwrite/Promises/Promise.php similarity index 54% rename from src/Appwrite/GraphQL/Promises/CoroutinePromise.php rename to src/Appwrite/Promises/Promise.php index 51c5b38dc5..3d345c6db1 100644 --- a/src/Appwrite/GraphQL/Promises/CoroutinePromise.php +++ b/src/Appwrite/Promises/Promise.php @@ -1,18 +1,8 @@ setResult($value); $this->setState(self::STATE_REJECTED); }; - \go(function () use ($executor, $resolve, $reject) { - try { - $executor($resolve, $reject); - } catch (\Throwable $exception) { - $reject($exception); - } - }); + $this->execute($executor, $resolve, $reject); } + abstract protected function execute( + callable $executor, + callable $resolve, + callable $reject + ): void; + /** * Create a new promise from the given callable. * * @param callable $promise - * @return CoroutinePromise + * @return self */ - final public static function create(callable $promise): CoroutinePromise + public static function create(callable $promise): self { return new static($promise); } @@ -59,9 +49,9 @@ class CoroutinePromise * Resolve promise with given value. * * @param mixed $value - * @return CoroutinePromise + * @return self */ - final public static function resolve(mixed $value): CoroutinePromise + public static function resolve(mixed $value): self { return new static(function (callable $resolve) use ($value) { $resolve($value); @@ -72,9 +62,9 @@ class CoroutinePromise * Rejects the promise with the given reason. * * @param mixed $value - * @return CoroutinePromise + * @return self */ - final public static function reject(mixed $value): CoroutinePromise + public static function reject(mixed $value): self { return new static(function (callable $resolve, callable $reject) use ($value) { $reject($value); @@ -85,9 +75,9 @@ class CoroutinePromise * Catch any exception thrown by the executor. * * @param callable $onRejected - * @return CoroutinePromise + * @return self */ - final public function catch(callable $onRejected): CoroutinePromise + public function catch(callable $onRejected): self { return $this->then(null, $onRejected); } @@ -97,10 +87,12 @@ class CoroutinePromise * * @param callable|null $onFulfilled * @param callable|null $onRejected - * @return CoroutinePromise + * @return self */ - public function then(?callable $onFulfilled = null, ?callable $onRejected = null): CoroutinePromise - { + public function then( + ?callable $onFulfilled = null, + ?callable $onRejected = null + ): self { if ($this->isRejected() && $onRejected === null) { return $this; } @@ -127,49 +119,10 @@ class CoroutinePromise /** * Returns a promise that completes when all passed in promises complete. * - * @param iterable|CoroutinePromise[] $promises - * @return CoroutinePromise + * @param iterable|self[] $promises + * @return self */ - public static function all(iterable $promises): CoroutinePromise - { - return self::create(function (callable $resolve, callable $reject) use ($promises) { - $ticks = count($promises); - - $firstError = null; - $channel = new Channel($ticks); - $result = []; - $key = 0; - - foreach ($promises as $promise) { - if (!$promise instanceof CoroutinePromise) { - $channel->close(); - throw new InvariantViolation('Expected instance of CoroutinePromise, got ' . Utils::printSafe($promise)); - } - $promise->then(function ($value) use ($key, &$result, $channel) { - $result[$key] = $value; - $channel->push(true); - return $value; - }, function ($error) use ($channel, &$firstError) { - $channel->push(true); - if ($firstError === null) { - $firstError = $error; - } - }); - $key++; - } - while ($ticks--) { - $channel->pop(); - } - $channel->close(); - - if ($firstError !== null) { - $reject($firstError); - return; - } - - $resolve($result); - }); - } + abstract public static function all(iterable $promises): self; /** * Set resolved result @@ -177,18 +130,20 @@ class CoroutinePromise * @param mixed $value * @return void */ - private function setResult(mixed $value): void + protected function setResult(mixed $value): void { - if (!$value instanceof Promise) { + if (!$value instanceof self) { $this->result = $value; return; } $resolved = false; + $callable = function ($value) use (&$resolved) { $this->setResult($value); $resolved = true; }; + $value->then($callable, $callable); while (!$resolved) { @@ -202,7 +157,7 @@ class CoroutinePromise * @param integer $state * @return void */ - final protected function setState(int $state): void + protected function setState(int $state): void { $this->state = $state; } @@ -212,7 +167,7 @@ class CoroutinePromise * * @return boolean */ - final protected function isPending(): bool + protected function isPending(): bool { return $this->state == self::STATE_PENDING; } @@ -222,7 +177,7 @@ class CoroutinePromise * * @return boolean */ - final protected function isFulfilled(): bool + protected function isFulfilled(): bool { return $this->state == self::STATE_FULFILLED; } @@ -232,7 +187,7 @@ class CoroutinePromise * * @return boolean */ - final protected function isRejected(): bool + protected function isRejected(): bool { return $this->state == self::STATE_REJECTED; } diff --git a/src/Appwrite/Promises/Swoole.php b/src/Appwrite/Promises/Swoole.php new file mode 100644 index 0000000000..c258ef6a5e --- /dev/null +++ b/src/Appwrite/Promises/Swoole.php @@ -0,0 +1,70 @@ +then(function ($value) use ($key, &$result, $channel) { + $result[$key] = $value; + $channel->push(true); + return $value; + }, function ($err) use ($channel, &$error) { + $channel->push(true); + if ($error === null) { + $error = $err; + } + }); + $key++; + } + while ($ticks--) { + $channel->pop(); + } + $channel->close(); + + if ($error !== null) { + $reject($error); + return; + } + + $resolve($result); + }); + } +}