diff --git a/src/Experimental/Executor/CoroutineExecutor.php b/src/Experimental/Executor/CoroutineExecutor.php index d985633..91091b4 100644 --- a/src/Experimental/Executor/CoroutineExecutor.php +++ b/src/Experimental/Executor/CoroutineExecutor.php @@ -293,13 +293,17 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation $strand->stack[$strand->depth++] = $strand->current; $strand->current = $value; goto START; - } elseif ($this->promiseAdapter->isThenable($value)) { + } elseif ($this->isPromise($value)) { // !!! increment pending before calling ->then() as it may invoke the callback right away ++$this->pending; + if (! $value instanceof Promise) { + $value = $this->promiseAdapter->convertThenable($value); + } + $this->promiseAdapter - ->convertThenable($value) ->then( + $value, function ($value) use ($strand) { $strand->success = true; $strand->value = $value; @@ -478,7 +482,7 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation private function completeValueFast(CoroutineContext $ctx, Type $type, $value, array $path, &$returnValue) : bool { // special handling of Throwable inherited from JS reference implementation, but makes no sense in this PHP - if ($this->promiseAdapter->isThenable($value) || $value instanceof Throwable) { + if ($this->isPromise($value) || $value instanceof Throwable) { return false; } @@ -574,7 +578,7 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation // !!! $value might be promise, yield to resolve try { - if ($this->promiseAdapter->isThenable($value)) { + if ($this->isPromise($value)) { $value = yield $value; } } catch (Throwable $reason) { @@ -931,4 +935,14 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation return $selectedType; } + + /** + * @param mixed $value + * + * @return bool + */ + private function isPromise($value) + { + return $value instanceof Promise || $this->promiseAdapter->isThenable($value); + } } diff --git a/tests/GraphQLTest.php b/tests/GraphQLTest.php new file mode 100644 index 0000000..13193b5 --- /dev/null +++ b/tests/GraphQLTest.php @@ -0,0 +1,47 @@ + new ObjectType( + [ + 'name' => 'Query', + 'fields' => [ + 'sayHi' => [ + 'type' => Type::nonNull(Type::string()), + 'args' => [ + 'name' => [ + 'type' => Type::nonNull(Type::string()), + ], + ], + 'resolve' => static function ($value, $args) use ($promiseAdapter) { + return $promiseAdapter->createFulfilled(sprintf('Hi %s!', $args['name'])); + }, + ], + ], + ] + ), + ] + ); + + $promise = GraphQL::promiseToExecute($promiseAdapter, $schema, '{ sayHi(name: "John") }'); + $result = $promiseAdapter->wait($promise); + self::assertSame(['data' => ['sayHi' => 'Hi John!']], $result->toArray()); + } +}