invariant() public static method

public static invariant ( $test, string $message = '' )
$test
$message string
Example #1
0
 /**
  * Prepares an object map of variables of the correct type based on the provided
  * variable definitions and arbitrary input. If the input cannot be coerced
  * to match the variable definitions, a Error will be thrown.
  *
  * @param Schema $schema
  * @param VariableDefinitionNode[] $definitionNodes
  * @param array $inputs
  * @return array
  * @throws Error
  */
 public static function getVariableValues(Schema $schema, $definitionNodes, array $inputs)
 {
     $coercedValues = [];
     foreach ($definitionNodes as $definitionNode) {
         $varName = $definitionNode->variable->name->value;
         $varType = Utils\TypeInfo::typeFromAST($schema, $definitionNode->type);
         if (!Type::isInputType($varType)) {
             throw new Error('Variable "$' . $varName . '" expected value of type ' . '"' . Printer::doPrint($definitionNode->type) . '" which cannot be used as an input type.', [$definitionNode->type]);
         }
         if (!array_key_exists($varName, $inputs)) {
             $defaultValue = $definitionNode->defaultValue;
             if ($defaultValue) {
                 $coercedValues[$varName] = Utils\AST::valueFromAST($defaultValue, $varType);
             }
             if ($varType instanceof NonNull) {
                 throw new Error('Variable "$' . $varName . '" of required type ' . '"' . Utils::printSafe($varType) . '" was not provided.', [$definitionNode]);
             }
         } else {
             $value = $inputs[$varName];
             $errors = self::isValidPHPValue($value, $varType);
             if (!empty($errors)) {
                 $message = "\n" . implode("\n", $errors);
                 throw new Error('Variable "$' . $varName . '" got invalid value ' . json_encode($value) . '.' . $message, [$definitionNode]);
             }
             $coercedValue = self::coerceValue($varType, $value);
             Utils::invariant($coercedValue !== Utils::undefined(), 'Should have reported error.');
             $coercedValues[$varName] = $coercedValue;
         }
     }
     return $coercedValues;
 }
Example #2
0
 /**
  * ScalarType constructor.
  */
 public function __construct()
 {
     if (!isset($this->name)) {
         $this->name = $this->tryInferName();
     }
     Utils::invariant($this->name, 'Type must be named.');
 }
Example #3
0
 /**
  * @param $name
  * @return FieldDefinition
  * @throws \Exception
  */
 public function getField($name)
 {
     if (null === $this->fields) {
         $this->getFields();
     }
     Utils::invariant(isset($this->fields[$name]), 'Field "%s" is not defined for type "%s"', $name, $this->name);
     return $this->fields[$name];
 }
 private function getTypeASTName(Type $typeAST)
 {
     if ($typeAST->kind === Node::NAME) {
         return $typeAST->value;
     }
     Utils::invariant($typeAST->type, 'Must be wrapping type');
     return $this->getTypeASTName($typeAST->type);
 }
Example #5
0
 /**
  * @param Schema $schema
  * @param $inputTypeAst
  * @return ListOfType|NonNull|Name
  * @throws \Exception
  */
 public static function typeFromAST(Schema $schema, $inputTypeAst)
 {
     if ($inputTypeAst instanceof ListType) {
         $innerType = self::typeFromAST($schema, $inputTypeAst->type);
         return $innerType ? new ListOfType($innerType) : null;
     }
     if ($inputTypeAst instanceof NonNullType) {
         $innerType = self::typeFromAST($schema, $inputTypeAst->type);
         return $innerType ? new NonNull($innerType) : null;
     }
     Utils::invariant($inputTypeAst instanceof Name, 'Must be a type name');
     return $schema->getType($inputTypeAst->value);
 }
Example #6
0
 /**
  * ObjectType constructor.
  * @param array $config
  */
 public function __construct(array $config)
 {
     if (!isset($config['name'])) {
         $config['name'] = $this->tryInferName();
     }
     Utils::invariant(!empty($config['name']), 'Every type is expected to have name');
     // Note: this validation is disabled by default, because it is resource-consuming
     // TODO: add bin/validate script to check if schema is valid during development
     Config::validate($config, ['name' => Config::NAME | Config::REQUIRED, 'fields' => Config::arrayOf(FieldDefinition::getDefinition(), Config::KEY_AS_NAME | Config::MAYBE_THUNK | Config::MAYBE_TYPE), 'description' => Config::STRING, 'interfaces' => Config::arrayOf(Config::INTERFACE_TYPE, Config::MAYBE_THUNK), 'isTypeOf' => Config::CALLBACK, 'resolveField' => Config::CALLBACK]);
     $this->name = $config['name'];
     $this->description = isset($config['description']) ? $config['description'] : null;
     $this->resolveFieldFn = isset($config['resolveField']) ? $config['resolveField'] : null;
     $this->config = $config;
 }
Example #7
0
 public function __construct($config)
 {
     Config::validate($config, ['name' => Config::STRING | Config::REQUIRED, 'types' => Config::arrayOf(Config::OBJECT_TYPE | Config::REQUIRED), 'resolveType' => Config::CALLBACK, 'description' => Config::STRING]);
     Utils::invariant(!empty($config['types']), "");
     /**
      * Optionally provide a custom type resolver function. If one is not provided,
      * the default implemenation will call `isTypeOf` on each implementing
      * Object type.
      */
     $this->name = $config['name'];
     $this->description = isset($config['description']) ? $config['description'] : null;
     $this->_types = $config['types'];
     $this->_resolveType = isset($config['resolveType']) ? $config['resolveType'] : null;
 }
Example #8
0
 /**
  * @return ObjectType[]
  */
 public function getTypes()
 {
     if (null === $this->types) {
         if ($this->config['types'] instanceof \Closure) {
             $types = call_user_func($this->config['types']);
         } else {
             $types = $this->config['types'];
         }
         Utils::invariant(is_array($types), 'Option "types" of union "%s" is expected to return array of types (or closure returning array of types)', $this->name);
         $this->types = [];
         foreach ($types as $type) {
             $this->types[] = Type::resolve($type);
         }
     }
     return $this->types;
 }
Example #9
0
 public function __invoke(ValidationContext $context)
 {
     return [Node::ARGUMENT => function (Argument $node) use($context) {
         $fieldDef = $context->getFieldDef();
         if ($fieldDef) {
             $argDef = null;
             foreach ($fieldDef->args as $arg) {
                 if ($arg->name === $node->name->value) {
                     $argDef = $arg;
                     break;
                 }
             }
             if (!$argDef) {
                 $parentType = $context->getParentType();
                 Utils::invariant($parentType);
                 return new Error(Messages::unknownArgMessage($node->name->value, $fieldDef->name, $parentType->name), [$node]);
             }
         }
     }];
 }
 public function __invoke(ValidationContext $context)
 {
     return [NodeKind::ARGUMENT => function (ArgumentNode $node, $key, $parent, $path, $ancestors) use($context) {
         $argumentOf = $ancestors[count($ancestors) - 1];
         if ($argumentOf->kind === NodeKind::FIELD) {
             $fieldDef = $context->getFieldDef();
             if ($fieldDef) {
                 $fieldArgDef = null;
                 foreach ($fieldDef->args as $arg) {
                     if ($arg->name === $node->name->value) {
                         $fieldArgDef = $arg;
                         break;
                     }
                 }
                 if (!$fieldArgDef) {
                     $parentType = $context->getParentType();
                     Utils::invariant($parentType);
                     $context->reportError(new Error(self::unknownArgMessage($node->name->value, $fieldDef->name, $parentType->name), [$node]));
                 }
             }
         } else {
             if ($argumentOf->kind === NodeKind::DIRECTIVE) {
                 $directive = $context->getDirective();
                 if ($directive) {
                     $directiveArgDef = null;
                     foreach ($directive->args as $arg) {
                         if ($arg->name === $node->name->value) {
                             $directiveArgDef = $arg;
                             break;
                         }
                     }
                     if (!$directiveArgDef) {
                         $context->reportError(new Error(self::unknownDirectiveArgMessage($node->name->value, $directive->name), [$node]));
                     }
                 }
             }
         }
     }];
 }
Example #11
0
 private function _extractTypes($type, &$map)
 {
     if (!$type) {
         return $map;
     }
     if ($type instanceof WrappingType) {
         return $this->_extractTypes($type->getWrappedType(), $map);
     }
     if (!empty($map[$type->name])) {
         Utils::invariant($map[$type->name] === $type, "Schema must contain unique named types but contains multiple types named \"{$type}\".");
         return $map;
     }
     $map[$type->name] = $type;
     $nestedTypes = [];
     if ($type instanceof InterfaceType || $type instanceof UnionType) {
         $nestedTypes = $type->getPossibleTypes();
     }
     if ($type instanceof ObjectType) {
         $nestedTypes = array_merge($nestedTypes, $type->getInterfaces());
     }
     if ($type instanceof ObjectType || $type instanceof InterfaceType || $type instanceof InputObjectType) {
         foreach ((array) $type->getFields() as $fieldName => $field) {
             if (isset($field->args)) {
                 $fieldArgTypes = array_map(function ($arg) {
                     return $arg->getType();
                 }, $field->args);
                 $nestedTypes = array_merge($nestedTypes, $fieldArgTypes);
             }
             $nestedTypes[] = $field->getType();
         }
     }
     foreach ($nestedTypes as $type) {
         $this->_extractTypes($type, $map);
     }
     return $map;
 }
Example #12
0
 /**
  * @param bool $recurse
  * @return mixed
  * @throws \Exception
  */
 public function getWrappedType($recurse = false)
 {
     $type = Type::resolve($this->ofType);
     Utils::invariant(!$type instanceof NonNull, 'Cannot nest NonNull inside NonNull');
     return $recurse && $type instanceof WrappingType ? $type->getWrappedType($recurse) : $type;
 }
Example #13
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);
 }
Example #14
0
 public static function resolve($type)
 {
     if (is_callable($type)) {
         $type = $type();
     }
     Utils::invariant($type instanceof Type, 'Expecting instance of ' . __CLASS__ . ' (or callable returning instance of that type), got "%s"', Utils::getVariableType($type));
     return $type;
 }
Example #15
0
 /**
  * Complete a list value by completing each item in the list with the
  * inner type
  *
  * @param ExecutionContext $exeContext
  * @param ListOfType $returnType
  * @param $fieldNodes
  * @param ResolveInfo $info
  * @param array $path
  * @param $result
  * @return array|Promise
  * @throws \Exception
  */
 private static function completeListValue(ExecutionContext $exeContext, ListOfType $returnType, $fieldNodes, ResolveInfo $info, $path, &$result)
 {
     $itemType = $returnType->getWrappedType();
     Utils::invariant(is_array($result) || $result instanceof \Traversable, 'User Error: expected iterable, but did not find one for field ' . $info->parentType . '.' . $info->fieldName . '.');
     $containsPromise = false;
     $i = 0;
     $completedItems = [];
     foreach ($result as $item) {
         $fieldPath = $path;
         $fieldPath[] = $i++;
         $completedItem = self::completeValueCatchingError($exeContext, $itemType, $fieldNodes, $info, $fieldPath, $item);
         if (!$containsPromise && self::$promiseAdapter->isPromise($completedItem)) {
             $containsPromise = true;
         }
         $completedItems[] = $completedItem;
     }
     return $containsPromise ? self::$promiseAdapter->createPromiseAll($completedItems) : $completedItems;
 }
 /**
  * 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;
 }
Example #17
0
 /**
  * @param ObjectType $object
  * @param InterfaceType $iface
  * @throws \Exception
  */
 private static function assertObjectImplementsInterface(Schema $schema, ObjectType $object, InterfaceType $iface)
 {
     $objectFieldMap = $object->getFields();
     $ifaceFieldMap = $iface->getFields();
     foreach ($ifaceFieldMap as $fieldName => $ifaceField) {
         Utils::invariant(isset($objectFieldMap[$fieldName]), "\"{$iface}\" expects field \"{$fieldName}\" but \"{$object}\" does not provide it");
         /** @var $ifaceField FieldDefinition */
         /** @var $objectField FieldDefinition */
         $objectField = $objectFieldMap[$fieldName];
         Utils::invariant(Utils\TypeInfo::isTypeSubTypeOf($schema, $objectField->getType(), $ifaceField->getType()), "{$iface}.{$fieldName} expects type \"{$ifaceField->getType()}\" but " . "{$object}.{$fieldName} provides type \"{$objectField->getType()}\".");
         foreach ($ifaceField->args as $ifaceArg) {
             /** @var $ifaceArg FieldArgument */
             /** @var $objectArg FieldArgument */
             $argName = $ifaceArg->name;
             $objectArg = $objectField->getArg($argName);
             // Assert interface field arg exists on object field.
             Utils::invariant($objectArg, "{$iface}.{$fieldName} expects argument \"{$argName}\" but {$object}.{$fieldName} does not provide it.");
             // Assert interface field arg type matches object field arg type.
             // (invariant)
             Utils::invariant(Utils\TypeInfo::isEqualType($ifaceArg->getType(), $objectArg->getType()), "{$iface}.{$fieldName}({$argName}:) expects type \"{$ifaceArg->getType()}\" " . "but {$object}.{$fieldName}({$argName}:) provides " . "type \"{$objectArg->getType()}\"");
             // Assert argument set invariance.
             foreach ($objectField->args as $objectArg) {
                 $argName = $objectArg->name;
                 $ifaceArg = $ifaceField->getArg($argName);
                 Utils::invariant($ifaceArg, "{$iface}.{$fieldName} does not define argument \"{$argName}\" but " . "{$object}.{$fieldName} provides it.");
             }
         }
     }
 }
Example #18
0
 public function __construct(Type $querySchema = null, Type $mutationSchema = null)
 {
     Utils::invariant($querySchema || $mutationSchema, "Either query or mutation type must be set");
     $this->querySchema = $querySchema;
     $this->mutationSchema = $mutationSchema;
 }
Example #19
0
 public function getField($name)
 {
     Utils::invariant(isset($this->_fields[$name]), 'Field "%s" is not defined for type "%s"', $name, $this->name);
     return $this->_fields[$name];
 }
Example #20
0
 private static function _validateEntry($key, $value, $def, $pathStr)
 {
     $type = Utils::getVariableType($value);
     $err = 'Expecting %s at "' . $pathStr . '", but got "' . $type . '"';
     if ($def instanceof \stdClass) {
         if ($def->flags & self::REQUIRED === 0 && $value === null) {
             return;
         }
         Utils::invariant(is_array($value), $err, 'array');
         if (!empty($def->isMap)) {
             if ($def->flags & self::KEY_AS_NAME) {
                 $value += ['name' => $key];
             }
             self::_validateMap($value, $def->definition, $pathStr);
         } else {
             if (!empty($def->isArray)) {
                 if ($def->flags & self::REQUIRED) {
                     Utils::invariant(!empty($value), "Value at '{$pathStr}' cannot be empty array");
                 }
                 $err = "Each entry at '{$pathStr}' must be an array, but '%s' is '%s'";
                 foreach ($value as $arrKey => $arrValue) {
                     if (is_array($def->definition)) {
                         Utils::invariant(is_array($arrValue), $err, $arrKey, Utils::getVariableType($arrValue));
                         if ($def->flags & self::KEY_AS_NAME) {
                             $arrValue += ['name' => $arrKey];
                         }
                         self::_validateMap($arrValue, $def->definition, "{$pathStr}:{$arrKey}");
                     } else {
                         self::_validateEntry($arrKey, $arrValue, $def->definition, "{$pathStr}:{$arrKey}");
                     }
                 }
             } else {
                 throw new \Exception("Unexpected definition: " . print_r($def, true));
             }
         }
     } else {
         Utils::invariant(is_int($def), "Definition for '{$pathStr}' is expected to be single integer value");
         if ($def & self::REQUIRED) {
             Utils::invariant($value !== null, 'Value at "%s" can not be null', $pathStr);
         }
         switch (true) {
             case $def & self::ANY:
                 break;
             case $def & self::BOOLEAN:
                 Utils::invariant(is_bool($value), $err, 'boolean');
                 break;
             case $def & self::STRING:
                 Utils::invariant(is_string($value), $err, 'string');
                 break;
             case $def & self::NUMERIC:
                 Utils::invariant(is_numeric($value), $err, 'numeric');
                 break;
             case $def & self::FLOAT:
                 Utils::invariant(is_float($value), $err, 'float');
                 break;
             case $def & self::INT:
                 Utils::invariant(is_int($value), $err, 'int');
                 break;
             case $def & self::CALLBACK:
                 Utils::invariant(is_callable($value), $err, 'callable');
                 break;
             case $def & self::SCALAR:
                 Utils::invariant(is_scalar($value), $err, 'scalar');
                 break;
             case $def & self::INPUT_TYPE:
                 Utils::invariant(is_callable($value) || $value instanceof InputType, $err, 'callable or instance of GraphQL\\Type\\Definition\\InputType');
                 break;
             case $def & self::OUTPUT_TYPE:
                 Utils::invariant(is_callable($value) || $value instanceof OutputType, $err, 'callable or instance of GraphQL\\Type\\Definition\\OutputType');
                 break;
             case $def & self::INTERFACE_TYPE:
                 Utils::invariant(is_callable($value) || $value instanceof InterfaceType, $err, 'callable or instance of GraphQL\\Type\\Definition\\InterfaceType');
                 break;
             case $def & self::OBJECT_TYPE:
                 Utils::invariant(is_callable($value) || $value instanceof ObjectType, $err, 'callable or instance of GraphQL\\Type\\Definition\\ObjectType');
                 break;
             default:
                 throw new \Exception("Unexpected validation rule: " . $def);
         }
     }
 }
Example #21
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);
 }
Example #22
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);
 }
Example #23
0
 /**
  * Complete a list value by completing each item in the list with the
  * inner type
  *
  * @param ExecutionContext $exeContext
  * @param ListOfType $returnType
  * @param $fieldASTs
  * @param ResolveInfo $info
  * @param array $path
  * @param $result
  * @return array
  * @throws \Exception
  */
 private static function completeListValue(ExecutionContext $exeContext, ListOfType $returnType, $fieldASTs, ResolveInfo $info, $path, &$result)
 {
     $itemType = $returnType->getWrappedType();
     Utils::invariant(is_array($result) || $result instanceof \Traversable, 'User Error: expected iterable, but did not find one for field ' . $info->parentType . '.' . $info->fieldName . '.');
     $i = 0;
     $tmp = [];
     foreach ($result as $item) {
         $path[] = $i++;
         $tmp[] = self::completeValueCatchingError($exeContext, $itemType, $fieldASTs, $info, $path, $item);
     }
     return $tmp;
 }
Example #24
0
 /**
  * @param string $name
  * @return FieldDefinition
  * @throws \Exception
  */
 public function getField($name)
 {
     if (false === $this->_initialized) {
         $this->initialize();
     }
     Utils::invariant(isset($this->_fields[$name]), "Field '%s' is not defined for type '%s'", $name, $this->name);
     return $this->_fields[$name];
 }
Example #25
0
 /**
  * @param callable|Type $type
  */
 public function __construct($type)
 {
     Utils::invariant($type instanceof Type || is_callable($type), 'Expecting instance of GraphQL\\Type\\Definition\\Type or callable returning instance of that class');
     $this->ofType = $type;
 }
Example #26
0
 /**
  * @param string $name
  * @return FieldDefinition
  * @throws \Exception
  */
 public function getField($name)
 {
     if (null === $this->_fields) {
         $this->getFields();
     }
     Utils::invariant(isset($this->_fields[$name]), "Field '%s' is not defined for type '%s'", $name, $this->name);
     return $this->_fields[$name];
 }
Example #27
0
 /**
  * @param $typeName
  * @param $key
  * @param $value
  * @param $def
  * @param $pathStr
  * @throws \Exception
  */
 private static function validateEntry($typeName, $key, $value, $def, $pathStr)
 {
     $type = Utils::getVariableType($value);
     $err = 'Error in "' . $typeName . '" type definition: expecting %s at "' . $pathStr . '", but got "' . $type . '"';
     if ($def instanceof \stdClass) {
         if ($def->flags & self::REQUIRED === 0 && $value === null) {
             return;
         }
         if (($def->flags & self::MAYBE_THUNK) > 0) {
             Utils::invariant(is_array($value) || is_callable($value), $err, 'array or callable');
         } else {
             Utils::invariant(is_array($value), $err, 'array');
         }
         if (!empty($def->isMap)) {
             if ($def->flags & self::KEY_AS_NAME) {
                 $value += ['name' => $key];
             }
             self::validateMap($typeName, $value, $def->definition, $pathStr);
         } else {
             if (!empty($def->isArray)) {
                 if ($def->flags & self::REQUIRED) {
                     Utils::invariant(!empty($value), 'Error in "' . $typeName . '" type definition: ' . "Value at '{$pathStr}' cannot be empty array");
                 }
                 $err = 'Error in "' . $typeName . '" type definition: ' . "Each entry at '{$pathStr}' must be an array, but '%s' is '%s'";
                 foreach ($value as $arrKey => $arrValue) {
                     if (is_array($def->definition)) {
                         if ($def->flags & self::MAYBE_TYPE && $arrValue instanceof Type) {
                             $arrValue = ['type' => $arrValue];
                         }
                         if ($def->flags & self::MAYBE_NAME && is_string($arrValue)) {
                             $arrValue = ['name' => $arrValue];
                         }
                         Utils::invariant(is_array($arrValue), $err, $arrKey, Utils::getVariableType($arrValue));
                         if ($def->flags & self::KEY_AS_NAME) {
                             $arrValue += ['name' => $arrKey];
                         }
                         self::validateMap($typeName, $arrValue, $def->definition, "{$pathStr}:{$arrKey}");
                     } else {
                         self::validateEntry($typeName, $arrKey, $arrValue, $def->definition, "{$pathStr}:{$arrKey}");
                     }
                 }
             } else {
                 throw new InvariantViolation('Error in "' . $typeName . '" type definition: ' . "unexpected definition: " . print_r($def, true));
             }
         }
     } else {
         Utils::invariant(is_int($def), 'Error in "' . $typeName . '" type definition: ' . "Definition for '{$pathStr}' is expected to be single integer value");
         if ($def & self::REQUIRED) {
             Utils::invariant($value !== null, 'Value at "%s" can not be null', $pathStr);
         }
         if (null === $value) {
             return;
             // Allow nulls for non-required fields
         }
         switch (true) {
             case $def & self::ANY:
                 break;
             case $def & self::BOOLEAN:
                 Utils::invariant(is_bool($value), $err, 'boolean');
                 break;
             case $def & self::STRING:
                 Utils::invariant(is_string($value), $err, 'string');
                 break;
             case $def & self::NUMERIC:
                 Utils::invariant(is_numeric($value), $err, 'numeric');
                 break;
             case $def & self::FLOAT:
                 Utils::invariant(is_float($value), $err, 'float');
                 break;
             case $def & self::INT:
                 Utils::invariant(is_int($value), $err, 'int');
                 break;
             case $def & self::CALLBACK:
                 Utils::invariant(is_callable($value), $err, 'callable');
                 break;
             case $def & self::SCALAR:
                 Utils::invariant(is_scalar($value), $err, 'scalar');
                 break;
             case $def & self::NAME:
                 Utils::invariant(preg_match('~^[_a-zA-Z][_a-zA-Z0-9]*$~', $value), 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "%s" does not.', $value);
                 break;
             case $def & self::INPUT_TYPE:
                 Utils::invariant(is_callable($value) || $value instanceof InputType, $err, 'callable or InputType definition');
                 break;
             case $def & self::OUTPUT_TYPE:
                 Utils::invariant(is_callable($value) || $value instanceof OutputType, $err, 'callable or OutputType definition');
                 break;
             case $def & self::INTERFACE_TYPE:
                 Utils::invariant(is_callable($value) || $value instanceof InterfaceType, $err, 'callable or InterfaceType definition');
                 break;
             case $def & self::OBJECT_TYPE:
                 Utils::invariant(is_callable($value) || $value instanceof ObjectType, $err, 'callable or ObjectType definition');
                 break;
             default:
                 throw new InvariantViolation("Unexpected validation rule: " . $def);
         }
     }
 }
Example #28
0
 protected function __construct()
 {
     Utils::invariant($this->name, 'Type must be named.');
 }
Example #29
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);
 }
Example #30
0
 /**
  * @return Type|callable
  */
 public function getWrappedType()
 {
     $type = Type::resolve($this->ofType);
     Utils::invariant(!$type instanceof NonNull, 'Cannot nest NonNull inside NonNull');
     return $type;
 }