/** * @param $code * @return string */ public static function printCharCode($code) { if (null === $code) { return '<EOF>'; } return $code < 0x7f ? json_encode(Utils::chr($code)) : '"\\u' . dechex($code) . '"'; }
/** * ScalarType constructor. */ public function __construct() { if (!isset($this->name)) { $this->name = $this->tryInferName(); } Utils::invariant($this->name, 'Type must be named.'); }
public function parseValue($value) { if (!filter_var($value, FILTER_VALIDATE_EMAIL)) { throw new \Exception('Cannot represent value as email: ' . Utils::printSafe($value)); } return $value; }
public function __invoke(ValidationContext $context) { return [Node::FIELD => function (Field $fieldAST) use($context) { $fieldDef = $context->getFieldDef(); if (!$fieldDef) { return Visitor::skipNode(); } $errors = []; $argASTs = $fieldAST->arguments ?: []; $argASTMap = Utils::keyMap($argASTs, function (Argument $arg) { return $arg->name->value; }); foreach ($fieldDef->args as $argDef) { $argAST = isset($argASTMap[$argDef->name]) ? $argASTMap[$argDef->name] : null; if (!$argAST && $argDef->getType() instanceof NonNull) { $errors[] = new Error(Messages::missingArgMessage($fieldAST->name->value, $argDef->name, $argDef->getType()), [$fieldAST]); } } $argDefMap = Utils::keyMap($fieldDef->args, function ($def) { return $def->name; }); foreach ($argASTs as $argAST) { $argDef = $argDefMap[$argAST->name->value]; if ($argDef && !DocumentValidator::isValidLiteralValue($argAST->value, $argDef->getType())) { $errors[] = new Error(Messages::badValueMessage($argAST->name->value, $argDef->getType(), Printer::doPrint($argAST->value)), [$argAST->value]); } } return !empty($errors) ? $errors : null; }]; }
private function doTypesOverlap($t1, $t2) { if ($t1 === $t2) { return true; } if ($t1 instanceof ObjectType) { if ($t2 instanceof ObjectType) { return false; } return in_array($t1, $t2->getPossibleTypes()); } if ($t1 instanceof InterfaceType || $t1 instanceof UnionType) { if ($t2 instanceof ObjectType) { return in_array($t2, $t1->getPossibleTypes()); } $t1TypeNames = Utils::keyMap($t1->getPossibleTypes(), function ($type) { return $type->name; }); foreach ($t2->getPossibleTypes() as $type) { if (!empty($t1TypeNames[$type->name])) { return true; } } } return false; }
public function parseValue($value) { if (!is_string($value) || !filter_var($value, FILTER_VALIDATE_URL)) { throw new \Exception('Cannot represent value as URL:' . Utils::printSafe($value)); } return $value; }
/** * @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]; }
/** * Parses an externally provided value (query variable) to use as an input * * @param mixed $value * @return mixed */ public function parseValue($value) { if (!is_string($value) || !filter_var($value, FILTER_VALIDATE_URL)) { // quite naive, but after all this is example throw new \UnexpectedValueException("Cannot represent value as URL: " . Utils::printSafe($value)); } return $value; }
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); }
private function withModifiers($types) { return array_merge(Utils::map($types, function ($type) { return Type::listOf($type); }), Utils::map($types, function ($type) { return Type::nonNull($type); }), Utils::map($types, function ($type) { return Type::nonNull(Type::listOf($type)); })); }
/** * @param $value * @return float|null */ private function coerceFloat($value) { if ($value === '') { throw new InvariantViolation('Float cannot represent non numeric value: (empty string)'); } if (is_numeric($value) || $value === true || $value === false) { return (double) $value; } throw new InvariantViolation('Float cannot represent non numeric value: ' . Utils::printSafe($value)); }
public function __construct($config) { Config::validate($config, ['name' => Config::STRING | Config::REQUIRED, 'values' => Config::arrayOf(['name' => Config::STRING | Config::REQUIRED, 'value' => Config::ANY, 'deprecationReason' => Config::STRING, 'description' => Config::STRING], Config::KEY_AS_NAME), 'description' => Config::STRING]); $this->name = $config['name']; $this->description = isset($config['description']) ? $config['description'] : null; $this->_values = []; if (!empty($config['values'])) { foreach ($config['values'] as $name => $value) { $this->_values[] = Utils::assign(new EnumValueDefinition(), $value + ['name' => $name]); } } }
/** * Return implementations of `type` that include `fieldName` as a valid field. * * @param Schema $schema * @param AbstractType $type * @param $fieldName * @return array */ static function getImplementationsIncludingField(Schema $schema, AbstractType $type, $fieldName) { $types = $schema->getPossibleTypes($type); $types = Utils::filter($types, function ($t) use($fieldName) { return isset($t->getFields()[$fieldName]); }); $types = Utils::map($types, function ($t) { return $t->name; }); sort($types); return $types; }
/** * @param $value * @return int|null */ private function coerceInt($value) { if ($value === '') { throw new InvariantViolation('Int cannot represent non 32-bit signed integer value: (empty string)'); } if (false === $value || true === $value) { return (int) $value; } if (is_numeric($value) && $value <= self::MAX_INT && $value >= self::MIN_INT) { return (int) $value; } throw new InvariantViolation('Int cannot represent non 32-bit signed integer value: ' . Utils::printSafe($value)); }
public function __invoke(ValidationContext $context) { $operationCount = 0; return [NodeKind::DOCUMENT => function (DocumentNode $node) use(&$operationCount) { $tmp = Utils::filter($node->definitions, function ($definition) { return $definition->kind === NodeKind::OPERATION_DEFINITION; }); $operationCount = count($tmp); }, NodeKind::OPERATION_DEFINITION => function (OperationDefinitionNode $node) use(&$operationCount, $context) { if (!$node->name && $operationCount > 1) { $context->reportError(new Error(self::anonOperationNotAloneMessage(), [$node])); } }]; }
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; }
/** * @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; }
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])); } } } } }]; }
private function detectCycleRecursive(FragmentDefinition $fragment, ValidationContext $context) { $fragmentName = $fragment->name->value; $this->visitedFrags[$fragmentName] = true; $spreadNodes = $context->getFragmentSpreads($fragment); if (empty($spreadNodes)) { return; } $this->spreadPathIndexByName[$fragmentName] = count($this->spreadPath); for ($i = 0; $i < count($spreadNodes); $i++) { $spreadNode = $spreadNodes[$i]; $spreadName = $spreadNode->name->value; $cycleIndex = isset($this->spreadPathIndexByName[$spreadName]) ? $this->spreadPathIndexByName[$spreadName] : null; if ($cycleIndex === null) { $this->spreadPath[] = $spreadNode; if (empty($this->visitedFrags[$spreadName])) { $spreadFragment = $context->getFragment($spreadName); if ($spreadFragment) { $this->detectCycleRecursive($spreadFragment, $context); } } array_pop($this->spreadPath); } else { $cyclePath = array_slice($this->spreadPath, $cycleIndex); $nodes = $cyclePath; if (is_array($spreadNode)) { $nodes = array_merge($nodes, $spreadNode); } else { $nodes[] = $spreadNode; } $context->reportError(new Error(self::cycleErrorMessage($spreadName, Utils::map($cyclePath, function ($s) { return $s->name->value; })), $nodes)); } } $this->spreadPathIndexByName[$fragmentName] = null; }
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; }
/** * @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); } } }
public function __construct(array $data) { Utils::assign($this, $data); }
/** * @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; }
/** * Complete an Object value by executing all sub-selections. * * @param ExecutionContext $exeContext * @param ObjectType $returnType * @param $fieldNodes * @param ResolveInfo $info * @param array $path * @param $result * @return array|Promise|\stdClass * @throws Error */ private static function completeObjectValue(ExecutionContext $exeContext, ObjectType $returnType, $fieldNodes, ResolveInfo $info, $path, &$result) { // 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 === $returnType->isTypeOf($result, $exeContext->contextValue, $info)) { throw new Error("Expected value of type {$returnType} but got: " . Utils::getVariableType($result), $fieldNodes); } // Collect sub-fields to execute to complete this value. $subFieldNodes = new \ArrayObject(); $visitedFragmentNames = new \ArrayObject(); foreach ($fieldNodes as $fieldNode) { if (isset($fieldNode->selectionSet)) { $subFieldNodes = self::collectFields($exeContext, $returnType, $fieldNode->selectionSet, $subFieldNodes, $visitedFragmentNames); } } return self::executeFields($exeContext, $returnType, $result, $path, $subFieldNodes); }
/** * 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, $valueNode) { // A value must be provided if the type is non-null. if ($type instanceof NonNull) { if (!$valueNode || $valueNode instanceof NullValueNode) { return ['Expected "' . Utils::printSafe($type) . '", found null.']; } return static::isValidLiteralValue($type->getWrappedType(), $valueNode); } if (!$valueNode || $valueNode instanceof NullValueNode) { return []; } // This function only tests literals, and assumes variables will provide // values of the correct type. if ($valueNode instanceof VariableNode) { return []; } // Lists accept a non-list value as a list of one. if ($type instanceof ListOfType) { $itemType = $type->getWrappedType(); if ($valueNode instanceof ListValueNode) { $errors = []; foreach ($valueNode->values as $index => $itemNode) { $tmp = static::isValidLiteralValue($itemType, $itemNode); 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, $valueNode); } } // Input objects check each defined field and look for undefined fields. if ($type instanceof InputObjectType) { if ($valueNode->kind !== NodeKind::OBJECT) { return ["Expected \"{$type->name}\", found not an object."]; } $fields = $type->getFields(); $errors = []; // Ensure every provided field is defined. $fieldNodes = $valueNode->fields; foreach ($fieldNodes as $providedFieldNode) { if (empty($fields[$providedFieldNode->name->value])) { $errors[] = "In field \"{$providedFieldNode->name->value}\": Unknown field."; } } // Ensure every defined field is valid. $fieldNodeMap = Utils::keyMap($fieldNodes, function ($fieldNode) { return $fieldNode->name->value; }); foreach ($fields as $fieldName => $field) { $result = static::isValidLiteralValue($field->getType(), isset($fieldNodeMap[$fieldName]) ? $fieldNodeMap[$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($valueNode); if (null === $parseResult) { $printed = Printer::doPrint($valueNode); return ["Expected type \"{$type->name}\", found {$printed}."]; } return []; } throw new InvariantViolation('Must be input type'); }
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]; }
/** * 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); }
private function subfieldConflicts(array $conflicts, $responseName, FieldNode $ast1, FieldNode $ast2) { if (!empty($conflicts)) { return [[$responseName, Utils::map($conflicts, function ($conflict) { return $conflict[0]; })], array_reduce($conflicts, function ($allFields, $conflict) { return array_merge($allFields, $conflict[1]); }, [$ast1]), array_reduce($conflicts, function ($allFields, $conflict) { return array_merge($allFields, $conflict[2]); }, [$ast2])]; } }
/** * Produces a PHP value given a GraphQL Value AST. * * A GraphQL type must be provided, which will be used to interpret different * GraphQL Value literals. * * | GraphQL Value | PHP Value | * | -------------------- | ------------- | * | Input Object | Assoc Array | * | List | Array | * | Boolean | Boolean | * | String | String | * | Int / Float | Int / Float | * | Enum Value | Mixed | * * @param $valueAST * @param InputType $type * @param null $variables * @return array|null * @throws \Exception */ public static function valueFromAST($valueAST, InputType $type, $variables = null) { if ($type instanceof NonNull) { // Note: we're not checking that the result of valueFromAST is non-null. // We're assuming that this query has been validated and the value used // here is of the correct type. return self::valueFromAST($valueAST, $type->getWrappedType(), $variables); } if (!$valueAST) { return null; } if ($valueAST instanceof Variable) { $variableName = $valueAST->name->value; if (!$variables || !isset($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 instanceof ListValue) { return array_map(function ($itemAST) use($itemType, $variables) { return self::valueFromAST($itemAST, $itemType, $variables); }, $valueAST->values); } else { return [self::valueFromAST($valueAST, $itemType, $variables)]; } } if ($type instanceof InputObjectType) { $fields = $type->getFields(); if (!$valueAST instanceof ObjectValue) { return null; } $fieldASTs = Utils::keyMap($valueAST->fields, function ($field) { return $field->name->value; }); $values = []; foreach ($fields as $field) { $fieldAST = isset($fieldASTs[$field->name]) ? $fieldASTs[$field->name] : null; $fieldValue = self::valueFromAST($fieldAST ? $fieldAST->value : null, $field->getType(), $variables); if (null === $fieldValue) { $fieldValue = $field->defaultValue; } if (null !== $fieldValue) { $values[$field->name] = $fieldValue; } } return $values; } if ($type instanceof LeafType) { return $type->parseLiteral($valueAST); } throw new InvariantViolation('Must be input type'); }