/** * Complete a value of an abstract type by determining the runtime object type * of that value, then complete the value for that type. * * @param ExecutionContext $exeContext * @param AbstractType $returnType * @param $fieldNodes * @param ResolveInfo $info * @param array $path * @param $result * @return mixed * @throws Error */ private static function completeAbstractValue(ExecutionContext $exeContext, AbstractType $returnType, $fieldNodes, ResolveInfo $info, $path, &$result) { $runtimeType = $returnType->resolveType($result, $exeContext->contextValue, $info); if (null === $runtimeType) { $runtimeType = self::inferTypeOf($result, $exeContext->contextValue, $info, $returnType); } // If resolveType returns a string, we assume it's a ObjectType name. if (is_string($runtimeType)) { $runtimeType = $exeContext->schema->getType($runtimeType); } if (!$runtimeType instanceof ObjectType) { throw new Error("Abstract type {$returnType} must resolve to an Object type at runtime " . "for field {$info->parentType}.{$info->fieldName} with value \"" . print_r($result, true) . "\"," . "received \"{$runtimeType}\".", $fieldNodes); } if (!$exeContext->schema->isPossibleType($returnType, $runtimeType)) { throw new Error("Runtime Object type \"{$runtimeType}\" is not a possible type for \"{$returnType}\".", $fieldNodes); } return self::completeObjectValue($exeContext, $runtimeType, $fieldNodes, $info, $path, $result); }
/** * @param $value * @param AbstractType $abstractType * @return Type * @throws \Exception */ public static function getTypeOf($value, AbstractType $abstractType) { $possibleTypes = $abstractType->getPossibleTypes(); for ($i = 0; $i < count($possibleTypes); $i++) { /** @var ObjectType $type */ $type = $possibleTypes[$i]; $isTypeOf = $type->isTypeOf($value); if ($isTypeOf === null) { // TODO: move this to a JS impl specific type system validation step // so the error can be found before execution. throw new \Exception('Non-Object Type ' . $abstractType->name . ' does not implement ' . 'resolveType and Object Type ' . $type->name . ' does not implement ' . 'isTypeOf. There is no way to determine if a value is of this type.'); } if ($isTypeOf) { return $type; } } return null; }
/** * @param AbstractType $abstractType * @return ObjectType[] */ public function getPossibleTypes(AbstractType $abstractType) { if ($abstractType instanceof UnionType) { return $abstractType->getTypes(); } Utils::invariant($abstractType instanceof InterfaceType); return isset($this->implementations[$abstractType->name]) ? $this->implementations[$abstractType->name] : []; }