Ejemplo n.º 1
0
 /**
  * Provided a type and a super type, return true if the first type is either
  * equal or a subset of the second super type (covariant).
  */
 static function isTypeSubTypeOf(Schema $schema, Type $maybeSubType, Type $superType)
 {
     // Equivalent type is a valid subtype
     if ($maybeSubType === $superType) {
         return true;
     }
     // If superType is non-null, maybeSubType must also be nullable.
     if ($superType instanceof NonNull) {
         if ($maybeSubType instanceof NonNull) {
             return self::isTypeSubTypeOf($schema, $maybeSubType->getWrappedType(), $superType->getWrappedType());
         }
         return false;
     } else {
         if ($maybeSubType instanceof NonNull) {
             // If superType is nullable, maybeSubType may be non-null.
             return self::isTypeSubTypeOf($schema, $maybeSubType->getWrappedType(), $superType);
         }
     }
     // If superType type is a list, maybeSubType type must also be a list.
     if ($superType instanceof ListOfType) {
         if ($maybeSubType instanceof ListOfType) {
             return self::isTypeSubTypeOf($schema, $maybeSubType->getWrappedType(), $superType->getWrappedType());
         }
         return false;
     } else {
         if ($maybeSubType instanceof ListOfType) {
             // If superType is not a list, maybeSubType must also be not a list.
             return false;
         }
     }
     // If superType type is an abstract type, maybeSubType type may be a currently
     // possible object type.
     if (Type::isAbstractType($superType) && $maybeSubType instanceof ObjectType && $schema->isPossibleType($superType, $maybeSubType)) {
         return true;
     }
     // Otherwise, the child type is not a valid subtype of the parent type.
     return false;
 }
Ejemplo n.º 2
0
 /**
  * Implements the instructions for completeValue as defined in the
  * "Field entries" section of the spec.
  *
  * If the field type is Non-Null, then this recursively completes the value
  * for the inner type. It throws a field error if that completion returns null,
  * as per the "Nullability" section of the spec.
  *
  * If the field type is a List, then this recursively completes the value
  * for the inner type on each item in the list.
  *
  * If the field type is a Scalar or Enum, ensures the completed value is a legal
  * value of the type by calling the `serialize` method of GraphQL type
  * definition.
  *
  * If the field is an abstract type, determine the runtime type of the value
  * and then complete based on that type
  *
  * Otherwise, the field type expects a sub-selection set, and will complete the
  * value by evaluating all sub-selections.
  *
  * @param ExecutionContext $exeContext
  * @param Type $returnType
  * @param Field[] $fieldASTs
  * @param ResolveInfo $info
  * @param array $path
  * @param $result
  * @return array|null
  * @throws Error
  * @throws \Exception
  */
 private static function completeValue(ExecutionContext $exeContext, Type $returnType, $fieldASTs, ResolveInfo $info, $path, &$result)
 {
     if ($result instanceof \Exception) {
         throw $result;
     }
     // If field type is NonNull, complete for inner type, and throw field error
     // if result is null.
     if ($returnType instanceof NonNull) {
         $completed = self::completeValue($exeContext, $returnType->getWrappedType(), $fieldASTs, $info, $path, $result);
         if ($completed === null) {
             throw new InvariantViolation('Cannot return null for non-nullable field ' . $info->parentType . '.' . $info->fieldName . '.');
         }
         return $completed;
     }
     // If result is null-like, return null.
     if (null === $result) {
         return null;
     }
     // If field type is List, complete each item in the list with the inner type
     if ($returnType instanceof ListOfType) {
         return self::completeListValue($exeContext, $returnType, $fieldASTs, $info, $path, $result);
     }
     // If field type is Scalar or Enum, serialize to a valid value, returning
     // null if serialization is not possible.
     if ($returnType instanceof LeafType) {
         return self::completeLeafValue($returnType, $result);
     }
     if ($returnType instanceof AbstractType) {
         return self::completeAbstractValue($exeContext, $returnType, $fieldASTs, $info, $path, $result);
     }
     // Field type must be Object, Interface or Union and expect sub-selections.
     if ($returnType instanceof ObjectType) {
         return self::completeObjectValue($exeContext, $returnType, $fieldASTs, $info, $path, $result);
     }
     throw new \RuntimeException("Cannot complete value of unexpected type \"{$returnType}\".");
 }
Ejemplo n.º 3
0
 /**
  * Implements the instructions for completeValue as defined in the
  * "Field entries" section of the spec.
  *
  * If the field type is Non-Null, then this recursively completes the value
  * for the inner type. It throws a field error if that completion returns null,
  * as per the "Nullability" section of the spec.
  *
  * If the field type is a List, then this recursively completes the value
  * for the inner type on each item in the list.
  *
  * If the field type is a Scalar or Enum, ensures the completed value is a legal
  * value of the type by calling the `serialize` method of GraphQL type
  * definition.
  *
  * Otherwise, the field type expects a sub-selection set, and will complete the
  * value by evaluating all sub-selections.
  */
 private static function completeValue(ExecutionContext $exeContext, Type $returnType, $fieldASTs, ResolveInfo $info, &$result)
 {
     // If field type is NonNull, complete for inner type, and throw field error
     // if result is null.
     if ($returnType instanceof NonNull) {
         $completed = self::completeValue($exeContext, $returnType->getWrappedType(), $fieldASTs, $info, $result);
         if ($completed === null) {
             throw new Error('Cannot return null for non-nullable type.', $fieldASTs instanceof \ArrayObject ? $fieldASTs->getArrayCopy() : $fieldASTs);
         }
         return $completed;
     }
     // If result is null-like, return null.
     if (null === $result) {
         return null;
     }
     // If field type is List, complete each item in the list with the inner type
     if ($returnType instanceof ListOfType) {
         $itemType = $returnType->getWrappedType();
         Utils::invariant(is_array($result) || $result instanceof \Traversable, 'User Error: expected iterable, but did not find one.');
         $tmp = [];
         foreach ($result as $item) {
             $tmp[] = self::completeValueCatchingError($exeContext, $itemType, $fieldASTs, $info, $item);
         }
         return $tmp;
     }
     // If field type is Scalar or Enum, serialize to a valid value, returning
     // null if serialization is not possible.
     if ($returnType instanceof ScalarType || $returnType instanceof EnumType) {
         Utils::invariant(method_exists($returnType, 'serialize'), 'Missing serialize method on type');
         return $returnType->serialize($result);
     }
     // Field type must be Object, Interface or Union and expect sub-selections.
     if ($returnType instanceof ObjectType) {
         $objectType = $returnType;
     } else {
         if ($returnType instanceof AbstractType) {
             $objectType = $returnType->getObjectType($result, $info);
             if ($objectType && !$returnType->isPossibleType($objectType)) {
                 throw new Error("Runtime Object type \"{$objectType}\" is not a possible type for \"{$returnType}\".");
             }
         } else {
             $objectType = null;
         }
     }
     if (!$objectType) {
         return null;
     }
     // If there is an isTypeOf predicate function, call it with the
     // current result. If isTypeOf returns false, then raise an error rather
     // than continuing execution.
     if (false === $objectType->isTypeOf($result, $info)) {
         throw new Error("Expected value of type {$objectType} but got: {$result}.", $fieldASTs);
     }
     // Collect sub-fields to execute to complete this value.
     $subFieldASTs = new \ArrayObject();
     $visitedFragmentNames = new \ArrayObject();
     for ($i = 0; $i < count($fieldASTs); $i++) {
         $selectionSet = $fieldASTs[$i]->selectionSet;
         if ($selectionSet) {
             $subFieldASTs = self::collectFields($exeContext, $objectType, $selectionSet, $subFieldASTs, $visitedFragmentNames);
         }
     }
     return self::executeFields($exeContext, $objectType, $result, $subFieldASTs);
 }
Ejemplo n.º 4
0
 /**
  * Given a type and any value, return a runtime value coerced to match the type.
  */
 private static function coerceValue(Type $type, $value)
 {
     if ($type instanceof NonNull) {
         // Note: we're not checking that the result of coerceValue is non-null.
         // We only call this function after calling isValidValue.
         return self::coerceValue($type->getWrappedType(), $value);
     }
     if (null === $value) {
         return null;
     }
     if ($type instanceof ListOfType) {
         $itemType = $type->getWrappedType();
         // TODO: support iterable input
         if (is_array($value)) {
             return array_map(function ($item) use($itemType) {
                 return Values::coerceValue($itemType, $item);
             }, $value);
         } else {
             return [self::coerceValue($itemType, $value)];
         }
     }
     if ($type instanceof InputObjectType) {
         $fields = $type->getFields();
         $obj = [];
         foreach ($fields as $fieldName => $field) {
             $fieldValue = self::coerceValue($field->getType(), isset($value[$fieldName]) ? $value[$fieldName] : null);
             if (null === $fieldValue) {
                 $fieldValue = $field->defaultValue;
             }
             if (null !== $fieldValue) {
                 $obj[$fieldName] = $fieldValue;
             }
         }
         return $obj;
     }
     Utils::invariant($type instanceof ScalarType || $type instanceof EnumType, 'Must be input type');
     return $type->parseValue($value);
 }
Ejemplo n.º 5
0
 /**
  * Check if value is valid using GraphQL Type
  *
  * @param array      $value
  * @param Type $type
  *
  * @return boolean
  */
 private function isValidValue($value, Type $type)
 {
     if ($type instanceof NonNull) {
         if (null === $value) {
             return false;
         }
         return $this->isValidValue($value, $type->getWrappedType());
     }
     if ($value === null) {
         return true;
     }
     if ($type instanceof ListOfType) {
         $itemType = $type->getWrappedType();
         if (is_array($value)) {
             foreach ($value as $item) {
                 if (!$this->isValidValue($item, $itemType)) {
                     return false;
                 }
             }
             return true;
         } else {
             return $this->isValidValue($value, $itemType);
         }
     }
     if ($type instanceof InputObjectType) {
         if (!is_array($value)) {
             return false;
         }
         $fields = $type->getFields();
         $fieldMap = [];
         foreach ($fields as $fieldName => $field) {
             if (!$this->isValidValue(isset($value[$fieldName]) ? $value[$fieldName] : null, $field->getType())) {
                 return false;
             }
             $fieldMap[$field->name] = $field;
         }
         $diff = array_diff_key($value, $fieldMap);
         if (!empty($diff)) {
             return false;
         }
         return true;
     }
     Utils::invariant($type instanceof ScalarType || $type instanceof EnumType, 'Must be input type');
     return null !== $type->parseValue($value);
 }
Ejemplo n.º 6
0
 static function isValidLiteralValue($valueAST, Type $type)
 {
     // A value can only be not provided if the type is nullable.
     if (!$valueAST) {
         return !$type instanceof NonNull;
     }
     // Unwrap non-null.
     if ($type instanceof NonNull) {
         return self::isValidLiteralValue($valueAST, $type->getWrappedType());
     }
     // This function only tests literals, and assumes variables will provide
     // values of the correct type.
     if ($valueAST instanceof Variable) {
         return true;
     }
     if (!$valueAST instanceof Value) {
         return false;
     }
     // Lists accept a non-list value as a list of one.
     if ($type instanceof ListOfType) {
         $itemType = $type->getWrappedType();
         if ($valueAST instanceof ListValue) {
             foreach ($valueAST->values as $itemAST) {
                 if (!self::isValidLiteralValue($itemAST, $itemType)) {
                     return false;
                 }
             }
             return true;
         } else {
             return self::isValidLiteralValue($valueAST, $itemType);
         }
     }
     // Scalar/Enum input checks to ensure the type can serialize the value to
     // a non-null value.
     if ($type instanceof ScalarType || $type instanceof EnumType) {
         return $type->parseLiteral($valueAST) !== null;
     }
     // Input objects check each defined field, ensuring it is of the correct
     // type and provided if non-nullable.
     if ($type instanceof InputObjectType) {
         $fields = $type->getFields();
         if ($valueAST->kind !== Node::OBJECT) {
             return false;
         }
         $fieldASTs = $valueAST->fields;
         $fieldASTMap = Utils::keyMap($fieldASTs, function ($field) {
             return $field->name->value;
         });
         foreach ($fields as $fieldKey => $field) {
             $fieldName = $field->name ?: $fieldKey;
             if (!isset($fieldASTMap[$fieldName]) && $field->getType() instanceof NonNull) {
                 // Required fields missing
                 return false;
             }
         }
         foreach ($fieldASTs as $fieldAST) {
             if (empty($fields[$fieldAST->name->value]) || !self::isValidLiteralValue($fieldAST->value, $fields[$fieldAST->name->value]->getType())) {
                 return false;
             }
         }
         return true;
     }
     // Any other kind of type is not an input type, and a literal cannot be used.
     return false;
 }
 /**
  * Implements the instructions for completeValue as defined in the
  * "Field entries" section of the spec.
  *
  * If the field type is Non-Null, then this recursively completes the value
  * for the inner type. It throws a field error if that completion returns null,
  * as per the "Nullability" section of the spec.
  *
  * If the field type is a List, then this recursively completes the value
  * for the inner type on each item in the list.
  *
  * If the field type is a Scalar or Enum, ensures the completed value is a legal
  * value of the type by calling the `serialize` method of GraphQL type
  * definition.
  *
  * Otherwise, the field type expects a sub-selection set, and will complete the
  * value by evaluating all sub-selections.
  */
 private static function completeValue(ExecutionContext $exeContext, Type $returnType, $fieldASTs, ResolveInfo $info, &$result)
 {
     // If field type is NonNull, complete for inner type, and throw field error
     // if result is null.
     if ($returnType instanceof NonNull) {
         $completed = self::completeValue($exeContext, $returnType->getWrappedType(), $fieldASTs, $info, $result);
         if ($completed === null) {
             throw new Error('Cannot return null for non-nullable type.', $fieldASTs instanceof \ArrayObject ? $fieldASTs->getArrayCopy() : $fieldASTs);
         }
         return $completed;
     }
     // If result is null-like, return null.
     if (null === $result) {
         return null;
     }
     // If field type is Scalar or Enum, serialize to a valid value, returning
     // null if serialization is not possible.
     if ($returnType instanceof ScalarType || $returnType instanceof EnumType) {
         return $returnType->serialize($result);
     }
     // If field type is List, and return type is Composite - complete by executing these fields with list value as parameter
     if ($returnType instanceof ListOfType) {
         $itemType = $returnType->getWrappedType();
         Utils::invariant(is_array($result) || $result instanceof \Traversable, 'User Error: expected iterable, but did not find one.');
         // For Object[]:
         // Allow all object fields to process list value in it's `map` callback:
         if ($itemType instanceof ObjectType) {
             // Filter out nulls (as `map` doesn't expect it):
             $list = [];
             foreach ($result as $index => $item) {
                 if (null !== $item) {
                     $list[] = $item;
                 }
             }
             $subFieldASTs = self::collectSubFields($exeContext, $itemType, $fieldASTs);
             $mapped = self::executeFields($exeContext, $itemType, $list, $subFieldASTs);
             $i = 0;
             $completed = [];
             foreach ($result as $index => $item) {
                 if (null === $item) {
                     // Complete nulls separately
                     $completed[] = self::completeValueCatchingError($exeContext, $itemType, $fieldASTs, $info, $item);
                 } else {
                     // Assuming same order of mapped values
                     $completed[] = $mapped[$i++];
                 }
             }
             return $completed;
         } else {
             if ($itemType instanceof AbstractType) {
                 // Values sharded by ObjectType
                 $listPerObjectType = [];
                 // Helper structures to restore ordering after resolve calls
                 $resultTypeMap = [];
                 $typeNameMap = [];
                 $cursors = [];
                 $copied = [];
                 foreach ($result as $index => $item) {
                     $copied[$index] = $item;
                     if (null !== $item) {
                         $objectType = $itemType->getObjectType($item, $info);
                         if ($objectType && !$itemType->isPossibleType($objectType)) {
                             $exeContext->addError(new Error("Runtime Object type \"{$objectType}\" is not a possible type for \"{$itemType}\"."));
                             $copied[$index] = null;
                         } else {
                             $listPerObjectType[$objectType->name][] = $item;
                             $resultTypeMap[$index] = $objectType->name;
                             $typeNameMap[$objectType->name] = $objectType;
                         }
                     }
                 }
                 $mapped = [];
                 foreach ($listPerObjectType as $typeName => $list) {
                     $objectType = $typeNameMap[$typeName];
                     $subFieldASTs = self::collectSubFields($exeContext, $objectType, $fieldASTs);
                     $mapped[$typeName] = self::executeFields($exeContext, $objectType, $list, $subFieldASTs);
                     $cursors[$typeName] = 0;
                 }
                 // Restore order:
                 $completed = [];
                 foreach ($copied as $index => $item) {
                     if (null === $item) {
                         // Complete nulls separately
                         $completed[] = self::completeValueCatchingError($exeContext, $itemType, $fieldASTs, $info, $item);
                     } else {
                         $typeName = $resultTypeMap[$index];
                         $completed[] = $mapped[$typeName][$cursors[$typeName]++];
                     }
                 }
                 return $completed;
             } else {
                 // For simple lists:
                 $tmp = [];
                 foreach ($result as $item) {
                     $tmp[] = self::completeValueCatchingError($exeContext, $itemType, $fieldASTs, $info, $item);
                 }
                 return $tmp;
             }
         }
     }
     if ($returnType instanceof ObjectType) {
         $objectType = $returnType;
     } else {
         if ($returnType instanceof AbstractType) {
             $objectType = $returnType->getObjectType($result, $info);
             if ($objectType && !$returnType->isPossibleType($objectType)) {
                 throw new Error("Runtime Object type \"{$objectType}\" is not a possible type for \"{$returnType}\".");
             }
         } else {
             $objectType = null;
         }
     }
     if (!$objectType) {
         return null;
     }
     // If there is an isTypeOf predicate function, call it with the
     // current result. If isTypeOf returns false, then raise an error rather
     // than continuing execution.
     if (false === $objectType->isTypeOf($result, $info)) {
         throw new Error("Expected value of type {$objectType} but got: {$result}.", $fieldASTs);
     }
     // Collect sub-fields to execute to complete this value.
     $subFieldASTs = self::collectSubFields($exeContext, $objectType, $fieldASTs);
     $executed = self::executeFields($exeContext, $objectType, [$result], $subFieldASTs);
     return isset($executed[0]) ? $executed[0] : null;
 }
Ejemplo n.º 8
0
 /**
  * Given a type and a value AST node known to match this type, build a
  * runtime value.
  */
 private static function coerceValueAST(Type $type, $valueAST, $variables)
 {
     if ($type instanceof NonNull) {
         // Note: we're not checking that the result of coerceValueAST is non-null.
         // We're assuming that this query has been validated and the value used
         // here is of the correct type.
         return self::coerceValueAST($type->getWrappedType(), $valueAST, $variables);
     }
     if (!$valueAST) {
         return null;
     }
     if ($valueAST->kind === Node::VARIABLE) {
         $variableName = $valueAST->name->value;
         if (!isset($variables, $variables[$variableName])) {
             return null;
         }
         // Note: we're not doing any checking that this variable is correct. We're
         // assuming that this query has been validated and the variable usage here
         // is of the correct type.
         return $variables[$variableName];
     }
     if ($type instanceof ListOfType) {
         $itemType = $type->getWrappedType();
         if ($valueAST->kind === Node::ARR) {
             $tmp = [];
             foreach ($valueAST->values as $itemAST) {
                 $tmp[] = self::coerceValueAST($itemType, $itemAST, $variables);
             }
             return $tmp;
         } else {
             return [self::coerceValueAST($itemType, $valueAST, $variables)];
         }
     }
     if ($type instanceof InputObjectType) {
         $fields = $type->getFields();
         if ($valueAST->kind !== Node::OBJECT) {
             return null;
         }
         $fieldASTs = Utils::keyMap($valueAST->fields, function ($field) {
             return $field->name->value;
         });
         $obj = [];
         foreach ($fields as $fieldName => $field) {
             $fieldAST = $fieldASTs[$fieldName];
             $fieldValue = self::coerceValueAST($field->getType(), $fieldAST ? $fieldAST->value : null, $variables);
             $obj[$fieldName] = $fieldValue === null ? $field->defaultValue : $fieldValue;
         }
         return $obj;
     }
     if ($type instanceof ScalarType || $type instanceof EnumType) {
         $coerced = $type->coerceLiteral($valueAST);
         if (null !== $coerced) {
             return $coerced;
         }
     }
     return null;
 }
Ejemplo n.º 9
0
 /**
  * Given a type and any value, return a runtime value coerced to match the type.
  */
 private static function coerceValue(Type $type, $value)
 {
     $undefined = Utils::undefined();
     if ($value === $undefined) {
         return $undefined;
     }
     if ($type instanceof NonNull) {
         if ($value === null) {
             // Intentionally return no value.
             return $undefined;
         }
         return self::coerceValue($type->getWrappedType(), $value);
     }
     if (null === $value) {
         return null;
     }
     if ($type instanceof ListOfType) {
         $itemType = $type->getWrappedType();
         if (is_array($value) || $value instanceof \Traversable) {
             $coercedValues = [];
             foreach ($value as $item) {
                 $itemValue = self::coerceValue($itemType, $item);
                 if ($undefined === $itemValue) {
                     // Intentionally return no value.
                     return $undefined;
                 }
                 $coercedValues[] = $itemValue;
             }
             return $coercedValues;
         } else {
             $coercedValue = self::coerceValue($itemType, $value);
             if ($coercedValue === $undefined) {
                 // Intentionally return no value.
                 return $undefined;
             }
             return [$coercedValue];
         }
     }
     if ($type instanceof InputObjectType) {
         $coercedObj = [];
         $fields = $type->getFields();
         foreach ($fields as $fieldName => $field) {
             if (!array_key_exists($fieldName, $value)) {
                 if ($field->defaultValueExists()) {
                     $coercedObj[$fieldName] = $field->defaultValue;
                 } else {
                     if ($field->getType() instanceof NonNull) {
                         // Intentionally return no value.
                         return $undefined;
                     }
                 }
                 continue;
             }
             $fieldValue = self::coerceValue($field->getType(), $value[$fieldName]);
             if ($fieldValue === $undefined) {
                 // Intentionally return no value.
                 return $undefined;
             }
             $coercedObj[$fieldName] = $fieldValue;
         }
         return $coercedObj;
     }
     if ($type instanceof LeafType) {
         $parsed = $type->parseValue($value);
         if (null === $parsed) {
             // null or invalid values represent a failure to parse correctly,
             // in which case no value is returned.
             return $undefined;
         }
         return $parsed;
     }
     throw new InvariantViolation('Must be input type');
 }
Ejemplo n.º 10
0
 /**
  * Given a type and any value, return a runtime value coerced to match the type.
  */
 private static function coerceValue(Type $type, $value)
 {
     if ($type instanceof NonNull) {
         // Note: we're not checking that the result of coerceValue is non-null.
         // We only call this function after calling isValidPHPValue.
         return self::coerceValue($type->getWrappedType(), $value);
     }
     if (null === $value) {
         return null;
     }
     if ($type instanceof ListOfType) {
         $itemType = $type->getWrappedType();
         if (is_array($value) || $value instanceof \Traversable) {
             return Utils::map($value, function ($item) use($itemType) {
                 return Values::coerceValue($itemType, $item);
             });
         } else {
             return [self::coerceValue($itemType, $value)];
         }
     }
     if ($type instanceof InputObjectType) {
         $fields = $type->getFields();
         $obj = [];
         foreach ($fields as $fieldName => $field) {
             $fieldValue = self::coerceValue($field->getType(), isset($value[$fieldName]) ? $value[$fieldName] : null);
             if (null === $fieldValue) {
                 $fieldValue = $field->defaultValue;
             }
             if (null !== $fieldValue) {
                 $obj[$fieldName] = $fieldValue;
             }
         }
         return $obj;
     }
     if ($type instanceof LeafType) {
         return $type->parseValue($value);
     }
     throw new InvariantViolation('Must be input type');
 }
Ejemplo n.º 11
0
 /**
  * Implements the instructions for completeValue as defined in the
  * "Field entries" section of the spec.
  *
  * If the field type is Non-Null, then this recursively completes the value
  * for the inner type. It throws a field error if that completion returns null,
  * as per the "Nullability" section of the spec.
  *
  * If the field type is a List, then this recursively completes the value
  * for the inner type on each item in the list.
  *
  * If the field type is a Scalar or Enum, ensures the completed value is a legal
  * value of the type by calling the `coerce` method of GraphQL type definition.
  *
  * Otherwise, the field type expects a sub-selection set, and will complete the
  * value by evaluating all sub-selections.
  */
 private static function completeField(ExecutionContext $exeContext, Type $fieldType, $fieldASTs, &$result)
 {
     // If field type is NonNull, complete for inner type, and throw field error
     // if result is null.
     if ($fieldType instanceof NonNull) {
         $completed = self::completeField($exeContext, $fieldType->getWrappedType(), $fieldASTs, $result);
         if ($completed === null) {
             throw new Error('Cannot return null for non-nullable type.', $fieldASTs instanceof \ArrayObject ? $fieldASTs->getArrayCopy() : $fieldASTs);
         }
         return $completed;
     }
     // If result is null-like, return null.
     if (null === $result) {
         return null;
     }
     // If field type is List, complete each item in the list with the inner type
     if ($fieldType instanceof ListOfType) {
         $itemType = $fieldType->getWrappedType();
         Utils::invariant(is_array($result) || $result instanceof \Traversable, 'User Error: expected iterable, but did not find one.');
         $tmp = [];
         foreach ($result as $item) {
             $tmp[] = self::completeField($exeContext, $itemType, $fieldASTs, $item);
         }
         return $tmp;
     }
     // If field type is Scalar or Enum, coerce to a valid value, returning null
     // if coercion is not possible.
     if ($fieldType instanceof ScalarType || $fieldType instanceof EnumType) {
         Utils::invariant(method_exists($fieldType, 'coerce'), 'Missing coerce method on type');
         return $fieldType->coerce($result);
     }
     // Field type must be Object, Interface or Union and expect sub-selections.
     $objectType = $fieldType instanceof ObjectType ? $fieldType : ($fieldType instanceof InterfaceType || $fieldType instanceof UnionType ? $fieldType->resolveType($result) : null);
     if (!$objectType) {
         return null;
     }
     // Collect sub-fields to execute to complete this value.
     $subFieldASTs = new \ArrayObject();
     $visitedFragmentNames = new \ArrayObject();
     for ($i = 0; $i < count($fieldASTs); $i++) {
         $selectionSet = $fieldASTs[$i]->selectionSet;
         if ($selectionSet) {
             $subFieldASTs = self::collectFields($exeContext, $objectType, $selectionSet, $subFieldASTs, $visitedFragmentNames);
         }
     }
     return self::executeFields($exeContext, $objectType, $result, $subFieldASTs);
 }
Ejemplo n.º 12
0
 /**
  * Utility for validators which determines if a value literal AST is valid given
  * an input type.
  *
  * Note that this only validates literal values, variables are assumed to
  * provide values of the correct type.
  *
  * @return array
  */
 public static function isValidLiteralValue(Type $type, $valueAST)
 {
     // A value must be provided if the type is non-null.
     if ($type instanceof NonNull) {
         $wrappedType = $type->getWrappedType();
         if (!$valueAST) {
             if ($wrappedType->name) {
                 return ["Expected \"{$wrappedType->name}!\", found null."];
             }
             return ['Expected non-null value, found null.'];
         }
         return static::isValidLiteralValue($wrappedType, $valueAST);
     }
     if (!$valueAST) {
         return [];
     }
     // This function only tests literals, and assumes variables will provide
     // values of the correct type.
     if ($valueAST instanceof Variable) {
         return [];
     }
     // Lists accept a non-list value as a list of one.
     if ($type instanceof ListOfType) {
         $itemType = $type->getWrappedType();
         if ($valueAST instanceof ListValue) {
             $errors = [];
             foreach ($valueAST->values as $index => $itemAST) {
                 $tmp = static::isValidLiteralValue($itemType, $itemAST);
                 if ($tmp) {
                     $errors = array_merge($errors, Utils::map($tmp, function ($error) use($index) {
                         return "In element #{$index}: {$error}";
                     }));
                 }
             }
             return $errors;
         } else {
             return static::isValidLiteralValue($itemType, $valueAST);
         }
     }
     // Input objects check each defined field and look for undefined fields.
     if ($type instanceof InputObjectType) {
         if ($valueAST->kind !== Node::OBJECT) {
             return ["Expected \"{$type->name}\", found not an object."];
         }
         $fields = $type->getFields();
         $errors = [];
         // Ensure every provided field is defined.
         $fieldASTs = $valueAST->fields;
         foreach ($fieldASTs as $providedFieldAST) {
             if (empty($fields[$providedFieldAST->name->value])) {
                 $errors[] = "In field \"{$providedFieldAST->name->value}\": Unknown field.";
             }
         }
         // Ensure every defined field is valid.
         $fieldASTMap = Utils::keyMap($fieldASTs, function ($fieldAST) {
             return $fieldAST->name->value;
         });
         foreach ($fields as $fieldName => $field) {
             $result = static::isValidLiteralValue($field->getType(), isset($fieldASTMap[$fieldName]) ? $fieldASTMap[$fieldName]->value : null);
             if ($result) {
                 $errors = array_merge($errors, Utils::map($result, function ($error) use($fieldName) {
                     return "In field \"{$fieldName}\": {$error}";
                 }));
             }
         }
         return $errors;
     }
     if ($type instanceof LeafType) {
         // Scalar/Enum input checks to ensure the type can parse the value to
         // a non-null value.
         $parseResult = $type->parseLiteral($valueAST);
         if (null === $parseResult) {
             $printed = Printer::doPrint($valueAST);
             return ["Expected type \"{$type->name}\", found {$printed}."];
         }
         return [];
     }
     throw new InvariantViolation('Must be input type');
 }