src/Experimental/Executor/CoroutineExecutor.php
private function completeValue(CoroutineContext $ctx, Type $type, $value, array $path, ?array $nullFence)
    {
        $nonNull     = false;
        $returnValue = null;

        if ($type instanceof NonNull) {
            $nonNull = true;
            $type    = $type->getWrappedType();
        } else {
            $nullFence = $path;
        }

        // !!! $value might be promise, yield to resolve
        try {
            if ($this->promiseAdapter->isThenable($value)) {
                $value = yield $value;
            }
        } catch (Throwable $reason) {
            $this->addError(Error::createLocatedError(
                $reason,
                $ctx->shared->fieldNodes,
                $path
            ));
            if ($nonNull) {
                $returnValue = self::$undefined;
            } else {
                $returnValue = null;
            }
            goto CHECKED_RETURN;
        }

        if ($value === null) {
            $returnValue = $value;
            goto CHECKED_RETURN;
        } elseif ($value instanceof Throwable) {
            // special handling of Throwable inherited from JS reference implementation, but makes no sense in this PHP
            $this->addError(Error::createLocatedError(
                $value,
                $ctx->shared->fieldNodes,
                $path
            ));
            if ($nonNull) {
                $returnValue = self::$undefined;
            } else {
                $returnValue = null;
            }
            goto CHECKED_RETURN;
        }

        if ($type instanceof ListOfType) {
            $returnValue = [];
            $index       = -1;
            $itemType    = $type->getWrappedType();
            foreach ($value as $itemValue) {
                ++$index;

                $itemPath   = $path;
                $itemPath[] = $index; // !!! use arrays COW semantics

                try {
                    if (! $this->completeValueFast($ctx, $itemType, $itemValue, $itemPath, $itemReturnValue)) {
                        $itemReturnValue = yield $this->completeValue($ctx, $itemType, $itemValue, $itemPath, $nullFence);
                    }
                } catch (Throwable $reason) {
                    $this->addError(Error::createLocatedError(
                        $reason,
                        $ctx->shared->fieldNodes,
                        $itemPath
                    ));
                    $itemReturnValue = null;
                }
                if ($itemReturnValue === self::$undefined) {
                    $returnValue = self::$undefined;
                    goto CHECKED_RETURN;
                }
                $returnValue[$index] = $itemReturnValue;
            }

            goto CHECKED_RETURN;
        } else {
            if ($type !== $this->schema->getType($type->name)) {
                $hint = '';
                if ($this->schema->getConfig()->typeLoader) {
                    $hint = sprintf(
                        'Make sure that type loader returns the same instance as defined in %s.%s',
                        $ctx->type,
                        $ctx->shared->fieldName
                    );
                }
                $this->addError(Error::createLocatedError(
                    new InvariantViolation(
                        sprintf(
                            'Schema must contain unique named types but contains multiple types named "%s". %s ' .
                            '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
                            $type->name,
                            $hint
                        )
                    ),
                    $ctx->shared->fieldNodes,
                    $path
                ));

                $returnValue = null;
                goto CHECKED_RETURN;
            }

            if ($type instanceof LeafType) {
                try {
                    $returnValue = $type->serialize($value);
                } catch (Throwable $error) {
                    $this->addError(Error::createLocatedError(
                        new InvariantViolation(
                            'Expected a value of type "' . Utils::printSafe($type) . '" but received: ' . Utils::printSafe($value),
                            0,
                            $error
                        ),
                        $ctx->shared->fieldNodes,
                        $path
                    ));
                    $returnValue = null;
                }
                goto CHECKED_RETURN;
            } elseif ($type instanceof CompositeType) {
                /** @var ObjectType|null $objectType */
                $objectType = null;
                if ($type instanceof InterfaceType || $type instanceof UnionType) {
                    $objectType = $type->resolveType($value, $this->contextValue, $ctx->resolveInfo);

                    if ($objectType === null) {
                        $objectType = yield $this->resolveTypeSlow($ctx, $value, $type);
                    }

                    // !!! $objectType->resolveType() might return promise, yield to resolve
                    $objectType = yield $objectType;
                    if (is_string($objectType)) {
                        $objectType = $this->schema->getType($objectType);
                    }

                    if ($objectType === null) {
                        $this->addError(Error::createLocatedError(
                            sprintf(
                                'Composite type "%s" did not resolve concrete object type for value: %s.',
                                $type->name,
                                Utils::printSafe($value)
                            ),
                            $ctx->shared->fieldNodes,
                            $path
                        ));

                        $returnValue = self::$undefined;
                        goto CHECKED_RETURN;
                    } elseif (! $objectType instanceof ObjectType) {
                        $this->addError(Error::createLocatedError(
                            new InvariantViolation(sprintf(
                                'Abstract type %s must resolve to an Object type at ' .
                                'runtime for field %s.%s with value "%s", received "%s". ' .
                                'Either the %s type should provide a "resolveType" ' .
                                'function or each possible type should provide an "isTypeOf" function.',
                                $type,
                                $ctx->resolveInfo->parentType,
                                $ctx->resolveInfo->fieldName,
                                Utils::printSafe($value),
                                Utils::printSafe($objectType),
                                $type
                            )),
                            $ctx->shared->fieldNodes,
                            $path
                        ));

                        $returnValue = null;
                        goto CHECKED_RETURN;
                    } elseif (! $this->schema->isPossibleType($type, $objectType)) {
                        $this->addError(Error::createLocatedError(
                            new InvariantViolation(sprintf(
                                'Runtime Object type "%s" is not a possible type for "%s".',
                                $objectType,
                                $type
                            )),
                            $ctx->shared->fieldNodes,
                            $path
                        ));

                        $returnValue = null;
                        goto CHECKED_RETURN;
                    } elseif ($objectType !== $this->schema->getType($objectType->name)) {
                        $this->addError(Error::createLocatedError(
                            new InvariantViolation(
                                sprintf(
                                    'Schema must contain unique named types but contains multiple types named "%s". ' .
                                    'Make sure that `resolveType` function of abstract type "%s" returns the same ' .
                                    'type instance as referenced anywhere else within the schema ' .
                                    '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
                                    $objectType,
                                    $type
                                )
                            ),
                            $ctx->shared->fieldNodes,
                            $path
                        ));

                        $returnValue = null;
                        goto CHECKED_RETURN;
                    }
                } elseif ($type instanceof ObjectType) {
                    $objectType = $type;
                } else {
                    $this->addError(Error::createLocatedError(
                        sprintf(
                            'Unexpected field type "%s".',
                            Utils::printSafe($type)
                        ),
                        $ctx->shared->fieldNodes,
                        $path
                    ));

                    $returnValue = self::$undefined;
                    goto CHECKED_RETURN;
                }

                $typeCheck = $objectType->isTypeOf($value, $this->contextValue, $ctx->resolveInfo);
                if ($typeCheck !== null) {
                    // !!! $objectType->isTypeOf() might return promise, yield to resolve
                    $typeCheck = yield $typeCheck;
                    if (! $typeCheck) {
                        $this->addError(Error::createLocatedError(
                            sprintf('Expected value of type "%s" but got: %s.', $type->name, Utils::printSafe($value)),
                            $ctx->shared->fieldNodes,
                            $path
                        ));

                        $returnValue = null;
                        goto CHECKED_RETURN;
                    }
                }

                $returnValue = new stdClass();

                if ($ctx->shared->typeGuard2 === $objectType) {
                    foreach ($ctx->shared->childContextsIfType2 as $childCtx) {
                        $childCtx              = clone $childCtx;
                        $childCtx->type        = $objectType;
                        $childCtx->value       = $value;
                        $childCtx->result      = $returnValue;
                        $childCtx->path        = $path;
                        $childCtx->path[]      = $childCtx->shared->resultName; // !!! uses array COW semantics
                        $childCtx->nullFence   = $nullFence;
                        $childCtx->resolveInfo = null;

                        $this->queue->enqueue(new Strand($this->spawn($childCtx)));

                        // !!! assign null to keep object keys sorted
                        $returnValue->{$childCtx->shared->resultName} = null;
                    }
                } else {
                    $childContexts = [];

                    foreach ($this->collector->collectFields($objectType, $ctx->shared->mergedSelectionSet ?? $this->mergeSelectionSets($ctx)) as $childShared) {
                        /** @var CoroutineContextShared $childShared */

                        $childPath   = $path;
                        $childPath[] = $childShared->resultName; // !!! uses array COW semantics
                        $childCtx    = new CoroutineContext(
                            $childShared,
                            $objectType,
                            $value,
                            $returnValue,
                            $childPath,
                            $nullFence
                        );

                        $childContexts[] = $childCtx;

                        $this->queue->enqueue(new Strand($this->spawn($childCtx)));

                        // !!! assign null to keep object keys sorted
                        $returnValue->{$childShared->resultName} = null;
                    }

                    $ctx->shared->typeGuard2           = $objectType;
                    $ctx->shared->childContextsIfType2 = $childContexts;
                }

                goto CHECKED_RETURN;
            } else {
                $this->addError(Error::createLocatedError(
                    sprintf('Unhandled type "%s".', Utils::printSafe($type)),
                    $ctx->shared->fieldNodes,
                    $path
                ));

                $returnValue = null;
                goto CHECKED_RETURN;
            }
        }

        CHECKED_RETURN:
        if ($nonNull && $returnValue === null) {
            $this->addError(Error::createLocatedError(
                new InvariantViolation(sprintf(
                    'Cannot return null for non-nullable field %s.%s.',
                    $ctx->type->name,
                    $ctx->shared->fieldName
                )),
                $ctx->shared->fieldNodes,
                $path
            ));

            return self::$undefined;
        }

        return $returnValue;
    }
private function completeValue(CoroutineContext $ctx, Type $type, $value, array $path, ?array $nullFence)
    {
        $nonNull     = false;
        $returnValue = null;

        if ($type instanceof NonNull) {
            $nonNull = true;
            $type    = $type->getWrappedType();
        } else {
            $nullFence = $path;
        }

        // !!! $value might be promise, yield to resolve
        try {
            if ($this->promiseAdapter->isThenable($value)) {
                $value = yield $value;
            }
        } catch (Throwable $reason) {
            $this->addError(Error::createLocatedError(
                $reason,
                $ctx->shared->fieldNodes,
                $path
            ));
            if ($nonNull) {
                $returnValue = self::$undefined;
            } else {
                $returnValue = null;
            }
            goto CHECKED_RETURN;
        }

        if ($value === null) {
            $returnValue = $value;
            goto CHECKED_RETURN;
        } elseif ($value instanceof Throwable) {
            // special handling of Throwable inherited from JS reference implementation, but makes no sense in this PHP
            $this->addError(Error::createLocatedError(
                $value,
                $ctx->shared->fieldNodes,
                $path
            ));
            if ($nonNull) {
                $returnValue = self::$undefined;
            } else {
                $returnValue = null;
            }
            goto CHECKED_RETURN;
        }

        if ($type instanceof ListOfType) {
            $returnValue = [];
            $index       = -1;
            $itemType    = $type->getWrappedType();
            foreach ($value as $itemValue) {
                ++$index;

                $itemPath   = $path;
                $itemPath[] = $index; // !!! use arrays COW semantics

                try {
                    if (! $this->completeValueFast($ctx, $itemType, $itemValue, $itemPath, $itemReturnValue)) {
                        $itemReturnValue = yield $this->completeValue($ctx, $itemType, $itemValue, $itemPath, $nullFence);
                    }
                } catch (Throwable $reason) {
                    $this->addError(Error::createLocatedError(
                        $reason,
                        $ctx->shared->fieldNodes,
                        $itemPath
                    ));
                    $itemReturnValue = null;
                }
                if ($itemReturnValue === self::$undefined) {
                    $returnValue = self::$undefined;
                    goto CHECKED_RETURN;
                }
                $returnValue[$index] = $itemReturnValue;
            }

            goto CHECKED_RETURN;
        } else {
            if ($type !== $this->schema->getType($type->name)) {
                $hint = '';
                if ($this->schema->getConfig()->typeLoader) {
                    $hint = sprintf(
                        'Make sure that type loader returns the same instance as defined in %s.%s',
                        $ctx->type,
                        $ctx->shared->fieldName
                    );
                }
                $this->addError(Error::createLocatedError(
                    new InvariantViolation(
                        sprintf(
                            'Schema must contain unique named types but contains multiple types named "%s". %s ' .
                            '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
                            $type->name,
                            $hint
                        )
                    ),
                    $ctx->shared->fieldNodes,
                    $path
                ));

                $returnValue = null;
                goto CHECKED_RETURN;
            }

            if ($type instanceof LeafType) {
                try {
                    $returnValue = $type->serialize($value);
                } catch (Throwable $error) {
                    $this->addError(Error::createLocatedError(
                        new InvariantViolation(
                            'Expected a value of type "' . Utils::printSafe($type) . '" but received: ' . Utils::printSafe($value),
                            0,
                            $error
                        ),
                        $ctx->shared->fieldNodes,
                        $path
                    ));
                    $returnValue = null;
                }
                goto CHECKED_RETURN;
            } elseif ($type instanceof CompositeType) {
                /** @var ObjectType|null $objectType */
                $objectType = null;
                if ($type instanceof InterfaceType || $type instanceof UnionType) {
                    $objectType = $type->resolveType($value, $this->contextValue, $ctx->resolveInfo);

                    if ($objectType === null) {
                        $objectType = yield $this->resolveTypeSlow($ctx, $value, $type);
                    }

                    // !!! $objectType->resolveType() might return promise, yield to resolve
                    $objectType = yield $objectType;
                    if (is_string($objectType)) {
                        $objectType = $this->schema->getType($objectType);
                    }

                    if ($objectType === null) {
                        $this->addError(Error::createLocatedError(
                            sprintf(
                                'Composite type "%s" did not resolve concrete object type for value: %s.',
                                $type->name,
                                Utils::printSafe($value)
                            ),
                            $ctx->shared->fieldNodes,
                            $path
                        ));

                        $returnValue = self::$undefined;
                        goto CHECKED_RETURN;
                    } elseif (! $objectType instanceof ObjectType) {
                        $this->addError(Error::createLocatedError(
                            new InvariantViolation(sprintf(
                                'Abstract type %s must resolve to an Object type at ' .
                                'runtime for field %s.%s with value "%s", received "%s". ' .
                                'Either the %s type should provide a "resolveType" ' .
                                'function or each possible type should provide an "isTypeOf" function.',
                                $type,
                                $ctx->resolveInfo->parentType,
                                $ctx->resolveInfo->fieldName,
                                Utils::printSafe($value),
                                Utils::printSafe($objectType),
                                $type
                            )),
                            $ctx->shared->fieldNodes,
                            $path
                        ));

                        $returnValue = null;
                        goto CHECKED_RETURN;
                    } elseif (! $this->schema->isPossibleType($type, $objectType)) {
                        $this->addError(Error::createLocatedError(
                            new InvariantViolation(sprintf(
                                'Runtime Object type "%s" is not a possible type for "%s".',
                                $objectType,
                                $type
                            )),
                            $ctx->shared->fieldNodes,
                            $path
                        ));

                        $returnValue = null;
                        goto CHECKED_RETURN;
                    } elseif ($objectType !== $this->schema->getType($objectType->name)) {
                        $this->addError(Error::createLocatedError(
                            new InvariantViolation(
                                sprintf(
                                    'Schema must contain unique named types but contains multiple types named "%s". ' .
                                    'Make sure that `resolveType` function of abstract type "%s" returns the same ' .
                                    'type instance as referenced anywhere else within the schema ' .
                                    '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
                                    $objectType,
                                    $type
                                )
                            ),
                            $ctx->shared->fieldNodes,
                            $path
                        ));

                        $returnValue = null;
                        goto CHECKED_RETURN;
                    }
                } elseif ($type instanceof ObjectType) {
                    $objectType = $type;
                } else {
                    $this->addError(Error::createLocatedError(
                        sprintf(
                            'Unexpected field type "%s".',
                            Utils::printSafe($type)
                        ),
                        $ctx->shared->fieldNodes,
                        $path
                    ));

                    $returnValue = self::$undefined;
                    goto CHECKED_RETURN;
                }

                $typeCheck = $objectType->isTypeOf($value, $this->contextValue, $ctx->resolveInfo);
                if ($typeCheck !== null) {
                    // !!! $objectType->isTypeOf() might return promise, yield to resolve
                    $typeCheck = yield $typeCheck;
                    if (! $typeCheck) {
                        $this->addError(Error::createLocatedError(
                            sprintf('Expected value of type "%s" but got: %s.', $type->name, Utils::printSafe($value)),
                            $ctx->shared->fieldNodes,
                            $path
                        ));

                        $returnValue = null;
                        goto CHECKED_RETURN;
                    }
                }

                $returnValue = new stdClass();

                if ($ctx->shared->typeGuard2 === $objectType) {
                    foreach ($ctx->shared->childContextsIfType2 as $childCtx) {
                        $childCtx              = clone $childCtx;
                        $childCtx->type        = $objectType;
                        $childCtx->value       = $value;
                        $childCtx->result      = $returnValue;
                        $childCtx->path        = $path;
                        $childCtx->path[]      = $childCtx->shared->resultName; // !!! uses array COW semantics
                        $childCtx->nullFence   = $nullFence;
                        $childCtx->resolveInfo = null;

                        $this->queue->enqueue(new Strand($this->spawn($childCtx)));

                        // !!! assign null to keep object keys sorted
                        $returnValue->{$childCtx->shared->resultName} = null;
                    }
                } else {
                    $childContexts = [];

                    foreach ($this->collector->collectFields($objectType, $ctx->shared->mergedSelectionSet ?? $this->mergeSelectionSets($ctx)) as $childShared) {
                        /** @var CoroutineContextShared $childShared */

                        $childPath   = $path;
                        $childPath[] = $childShared->resultName; // !!! uses array COW semantics
                        $childCtx    = new CoroutineContext(
                            $childShared,
                            $objectType,
                            $value,
                            $returnValue,
                            $childPath,
                            $nullFence
                        );

                        $childContexts[] = $childCtx;

                        $this->queue->enqueue(new Strand($this->spawn($childCtx)));

                        // !!! assign null to keep object keys sorted
                        $returnValue->{$childShared->resultName} = null;
                    }

                    $ctx->shared->typeGuard2           = $objectType;
                    $ctx->shared->childContextsIfType2 = $childContexts;
                }

                goto CHECKED_RETURN;
            } else {
                $this->addError(Error::createLocatedError(
                    sprintf('Unhandled type "%s".', Utils::printSafe($type)),
                    $ctx->shared->fieldNodes,
                    $path
                ));

                $returnValue = null;
                goto CHECKED_RETURN;
            }
        }

        CHECKED_RETURN:
        if ($nonNull && $returnValue === null) {
            $this->addError(Error::createLocatedError(
                new InvariantViolation(sprintf(
                    'Cannot return null for non-nullable field %s.%s.',
                    $ctx->type->name,
                    $ctx->shared->fieldName
                )),
                $ctx->shared->fieldNodes,
                $path
            ));

            return self::$undefined;
        }

        return $returnValue;
    }
private function completeValue ( CoroutineContext $ ctx , Type $ type , $ value , array $ path , ? array $ nullFence ) { $ nonNull = false ; $ returnValue = null ; if ( $ type instanceof NonNull ) { $ nonNull = true ; $ type = $ type -> getWrappedType ( ) ; } else { $ nullFence = $ path ; } // !!! $value might be promise, yield to resolve try { if ( $ this -> promiseAdapter -> isThenable ( $ value ) ) { $ value = yield $ value ; } } catch ( Throwable $ reason ) { $ this -> addError ( Error :: createLocatedError ( $ reason , $ ctx -> shared -> fieldNodes , $ path ) ) ; if ( $ nonNull ) { $ returnValue = self :: $ undefined ; } else { $ returnValue = null ; } goto CHECKED_RETURN ; } if ( $ value === null ) { $ returnValue = $ value ; goto CHECKED_RETURN ; } elseif ( $ value instanceof Throwable ) { // special handling of Throwable inherited from JS reference implementation, but makes no sense in this PHP $ this -> addError ( Error :: createLocatedError ( $ value , $ ctx -> shared -> fieldNodes , $ path ) ) ; if ( $ nonNull ) { $ returnValue = self :: $ undefined ; } else { $ returnValue = null ; } goto CHECKED_RETURN ; } if ( $ type instanceof ListOfType ) { $ returnValue = [ ] ; $ index = - 1 ; $ itemType = $ type -> getWrappedType ( ) ; foreach ( $ value as $ itemValue ) { ++ $ index ; $ itemPath = $ path ; $ itemPath [ ] = $ index ; // !!! use arrays COW semantics try { if ( ! $ this -> completeValueFast ( $ ctx , $ itemType , $ itemValue , $ itemPath , $ itemReturnValue ) ) { $ itemReturnValue = yield $ this -> completeValue ( $ ctx , $ itemType , $ itemValue , $ itemPath , $ nullFence ) ; } } catch ( Throwable $ reason ) { $ this -> addError ( Error :: createLocatedError ( $ reason , $ ctx -> shared -> fieldNodes , $ itemPath ) ) ; $ itemReturnValue = null ; } if ( $ itemReturnValue === self :: $ undefined ) { $ returnValue = self :: $ undefined ; goto CHECKED_RETURN ; } $ returnValue [ $ index ] = $ itemReturnValue ; } goto CHECKED_RETURN ; } else { if ( $ type !== $ this -> schema -> getType ( $ type -> name ) ) { $ hint = '' ; if ( $ this -> schema -> getConfig ( ) -> typeLoader ) { $ hint = sprintf ( 'Make sure that type loader returns the same instance as defined in %s.%s' , $ ctx -> type , $ ctx -> shared -> fieldName ) ; } $ this -> addError ( Error :: createLocatedError ( new InvariantViolation ( sprintf ( 'Schema must contain unique named types but contains multiple types named "%s". %s ' . '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).' , $ type -> name , $ hint ) ) , $ ctx -> shared -> fieldNodes , $ path ) ) ; $ returnValue = null ; goto CHECKED_RETURN ; } if ( $ type instanceof LeafType ) { try { $ returnValue = $ type -> serialize ( $ value ) ; } catch ( Throwable $ error ) { $ this -> addError ( Error :: createLocatedError ( new InvariantViolation ( 'Expected a value of type "' . Utils :: printSafe ( $ type ) . '" but received: ' . Utils :: printSafe ( $ value ) , 0 , $ error ) , $ ctx -> shared -> fieldNodes , $ path ) ) ; $ returnValue = null ; } goto CHECKED_RETURN ; } elseif ( $ type instanceof CompositeType ) { /** @var ObjectType|null $objectType */ $ objectType = null ; if ( $ type instanceof InterfaceType || $ type instanceof UnionType ) { $ objectType = $ type -> resolveType ( $ value , $ this -> contextValue , $ ctx -> resolveInfo ) ; if ( $ objectType === null ) { $ objectType = yield $ this -> resolveTypeSlow ( $ ctx , $ value , $ type ) ; } // !!! $objectType->resolveType() might return promise, yield to resolve $ objectType = yield $ objectType ; if ( is_string ( $ objectType ) ) { $ objectType = $ this -> schema -> getType ( $ objectType ) ; } if ( $ objectType === null ) { $ this -> addError ( Error :: createLocatedError ( sprintf ( 'Composite type "%s" did not resolve concrete object type for value: %s.' , $ type -> name , Utils :: printSafe ( $ value ) ) , $ ctx -> shared -> fieldNodes , $ path ) ) ; $ returnValue = self :: $ undefined ; goto CHECKED_RETURN ; } elseif ( ! $ objectType instanceof ObjectType ) { $ this -> addError ( Error :: createLocatedError ( new InvariantViolation ( sprintf ( 'Abstract type %s must resolve to an Object type at ' . 'runtime for field %s.%s with value "%s", received "%s". ' . 'Either the %s type should provide a "resolveType" ' . 'function or each possible type should provide an "isTypeOf" function.' , $ type , $ ctx -> resolveInfo -> parentType , $ ctx -> resolveInfo -> fieldName , Utils :: printSafe ( $ value ) , Utils :: printSafe ( $ objectType ) , $ type ) ) , $ ctx -> shared -> fieldNodes , $ path ) ) ; $ returnValue = null ; goto CHECKED_RETURN ; } elseif ( ! $ this -> schema -> isPossibleType ( $ type , $ objectType ) ) { $ this -> addError ( Error :: createLocatedError ( new InvariantViolation ( sprintf ( 'Runtime Object type "%s" is not a possible type for "%s".' , $ objectType , $ type ) ) , $ ctx -> shared -> fieldNodes , $ path ) ) ; $ returnValue = null ; goto CHECKED_RETURN ; } elseif ( $ objectType !== $ this -> schema -> getType ( $ objectType -> name ) ) { $ this -> addError ( Error :: createLocatedError ( new InvariantViolation ( sprintf ( 'Schema must contain unique named types but contains multiple types named "%s". ' . 'Make sure that `resolveType` function of abstract type "%s" returns the same ' . 'type instance as referenced anywhere else within the schema ' . '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).' , $ objectType , $ type ) ) , $ ctx -> shared -> fieldNodes , $ path ) ) ; $ returnValue = null ; goto CHECKED_RETURN ; } } elseif ( $ type instanceof ObjectType ) { $ objectType = $ type ; } else { $ this -> addError ( Error :: createLocatedError ( sprintf ( 'Unexpected field type "%s".' , Utils :: printSafe ( $ type ) ) , $ ctx -> shared -> fieldNodes , $ path ) ) ; $ returnValue = self :: $ undefined ; goto CHECKED_RETURN ; } $ typeCheck = $ objectType -> isTypeOf ( $ value , $ this -> contextValue , $ ctx -> resolveInfo ) ; if ( $ typeCheck !== null ) { // !!! $objectType->isTypeOf() might return promise, yield to resolve $ typeCheck = yield $ typeCheck ; if ( ! $ typeCheck ) { $ this -> addError ( Error :: createLocatedError ( sprintf ( 'Expected value of type "%s" but got: %s.' , $ type -> name , Utils :: printSafe ( $ value ) ) , $ ctx -> shared -> fieldNodes , $ path ) ) ; $ returnValue = null ; goto CHECKED_RETURN ; } } $ returnValue = new stdClass ( ) ; if ( $ ctx -> shared -> typeGuard2 === $ objectType ) { foreach ( $ ctx -> shared -> childContextsIfType2 as $ childCtx ) { $ childCtx = clone $ childCtx ; $ childCtx -> type = $ objectType ; $ childCtx -> value = $ value ; $ childCtx -> result = $ returnValue ; $ childCtx -> path = $ path ; $ childCtx -> path [ ] = $ childCtx -> shared -> resultName ; // !!! uses array COW semantics $ childCtx -> nullFence = $ nullFence ; $ childCtx -> resolveInfo = null ; $ this -> queue -> enqueue ( new Strand ( $ this -> spawn ( $ childCtx ) ) ) ; // !!! assign null to keep object keys sorted $ returnValue -> { $ childCtx -> shared -> resultName } = null ; } } else { $ childContexts = [ ] ; foreach ( $ this -> collector -> collectFields ( $ objectType , $ ctx -> shared -> mergedSelectionSet ?? $ this -> mergeSelectionSets ( $ ctx ) ) as $ childShared ) { /** @var CoroutineContextShared $childShared */ $ childPath = $ path ; $ childPath [ ] = $ childShared -> resultName ; // !!! uses array COW semantics $ childCtx = new CoroutineContext ( $ childShared , $ objectType , $ value , $ returnValue , $ childPath , $ nullFence ) ; $ childContexts [ ] = $ childCtx ; $ this -> queue -> enqueue ( new Strand ( $ this -> spawn ( $ childCtx ) ) ) ; // !!! assign null to keep object keys sorted $ returnValue -> { $ childShared -> resultName } = null ; } $ ctx -> shared -> typeGuard2 = $ objectType ; $ ctx -> shared -> childContextsIfType2 = $ childContexts ; } goto CHECKED_RETURN ; } else { $ this -> addError ( Error :: createLocatedError ( sprintf ( 'Unhandled type "%s".' , Utils :: printSafe ( $ type ) ) , $ ctx -> shared -> fieldNodes , $ path ) ) ; $ returnValue = null ; goto CHECKED_RETURN ; } } CHECKED_RETURN : if ( $ nonNull && $ returnValue === null ) { $ this -> addError ( Error :: createLocatedError ( new InvariantViolation ( sprintf ( 'Cannot return null for non-nullable field %s.%s.' , $ ctx -> type -> name , $ ctx -> shared -> fieldName ) ) , $ ctx -> shared -> fieldNodes , $ path ) ) ; return self :: $ undefined ; } return $ returnValue ; }
@param mixed         $value
@param string[]      $path
@param string[]|null $nullFence

@return mixed
@param mixed $value @param string [] $path @param string [] |null $nullFence