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 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; }
/** * 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; }
/** * @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)); }
/** * @param $typeName * @param array $config * @param array $definition */ public static function validateField($typeName, array $config, array $definition) { if (self::$enableValidation) { if (!isset($config['name'])) { $pathStr = isset($config['type']) ? '(Unknown Field of type: ' . Utils::printSafe($config['type']) . ')' : '(Unknown Field)'; } else { $pathStr = ''; } self::validateMap($typeName ?: '(Unnamed Type)', $config, $definition, $pathStr); } }
/** * @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)); }
/** * Complete a Scalar or Enum by serializing to a valid value, returning * null if serialization is not possible. * * @param LeafType $returnType * @param $result * @return mixed * @throws \Exception */ private static function completeLeafValue(LeafType $returnType, &$result) { $serializedResult = $returnType->serialize($result); if ($serializedResult === null) { throw new InvariantViolation('Expected a value of type "' . Utils::printSafe($returnType) . '" but received: ' . Utils::printSafe($result)); } return $serializedResult; }
/** * 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'); }
/** * Produces a GraphQL Value AST given a PHP value. * * Optionally, a GraphQL type may be provided, which will be used to * disambiguate between value primitives. * * | PHP Value | GraphQL Value | * | ------------- | -------------------- | * | Object | Input Object | * | Assoc Array | Input Object | * | Array | List | * | Boolean | Boolean | * | String | String / Enum Value | * | Int | Int | * | Float | Int / Float | * | Mixed | Enum Value | * * @param $value * @param InputType $type * @return ObjectValue|ListValue|BooleanValue|IntValue|FloatValue|EnumValue|StringValue */ static function astFromValue($value, InputType $type) { if ($type instanceof NonNull) { // Note: we're not checking that the result is non-null. // This function is not responsible for validating the input value. return self::astFromValue($value, $type->getWrappedType()); } if ($value === null) { return null; } // Convert PHP array to GraphQL list. If the GraphQLType is a list, but // the value is not an array, convert the value using the list's item type. if ($type instanceof ListOfType) { $itemType = $type->getWrappedType(); if (is_array($value) || $value instanceof \Traversable) { $valuesASTs = []; foreach ($value as $item) { $itemAST = self::astFromValue($item, $itemType); if ($itemAST) { $valuesASTs[] = $itemAST; } } return new ListValue(['values' => $valuesASTs]); } return self::astFromValue($value, $itemType); } // Populate the fields of the input object by creating ASTs from each value // in the PHP object according to the fields in the input type. if ($type instanceof InputObjectType) { $isArrayLike = is_array($value) || $value instanceof \ArrayAccess; if ($value === null || !$isArrayLike && !is_object($value)) { return null; } $fields = $type->getFields(); $fieldASTs = []; foreach ($fields as $fieldName => $field) { if ($isArrayLike) { $fieldValue = isset($value[$fieldName]) ? $value[$fieldName] : null; } else { $fieldValue = isset($value->{$fieldName}) ? $value->{$fieldName} : null; } $fieldValue = self::astFromValue($fieldValue, $field->getType()); if ($fieldValue) { $fieldASTs[] = new ObjectField(['name' => new Name(['value' => $fieldName]), 'value' => $fieldValue]); } } return new ObjectValue(['fields' => $fieldASTs]); } // Since value is an internally represented value, it must be serialized // to an externally represented value before converting into an AST. if ($type instanceof LeafType) { $serialized = $type->serialize($value); } else { throw new InvariantViolation("Must provide Input Type, cannot use: " . Utils::printSafe($type)); } if (null === $serialized) { return null; } // Others serialize based on their corresponding PHP scalar types. if (is_bool($serialized)) { return new BooleanValue(['value' => $serialized]); } if (is_int($serialized)) { return new IntValue(['value' => $serialized]); } if (is_float($serialized)) { if ((int) $serialized == $serialized) { return new IntValue(['value' => $serialized]); } return new FloatValue(['value' => $serialized]); } if (is_string($serialized)) { // Enum types use Enum literals. if ($type instanceof EnumType) { return new EnumValue(['value' => $serialized]); } // ID types can use Int literals. $asInt = (int) $serialized; if ($type instanceof IDType && (string) $asInt === $serialized) { return new IntValue(['value' => $serialized]); } // Use json_encode, which uses the same string encoding as GraphQL, // then remove the quotes. return new StringValue(['value' => substr(json_encode($serialized), 1, -1)]); } throw new InvariantViolation('Cannot convert value to AST: ' . Utils::printSafe($serialized)); }
public static function _type() { if (!isset(self::$map['__Type'])) { self::$map['__Type'] = new ObjectType(['name' => '__Type', 'description' => 'The fundamental unit of any GraphQL Schema is the type. There are ' . 'many kinds of types in GraphQL as represented by the `__TypeKind` enum.' . "\n\n" . 'Depending on the kind of a type, certain fields describe ' . 'information about that type. Scalar types provide no information ' . 'beyond a name and description, while Enum types provide their values. ' . 'Object and Interface types provide the fields they describe. Abstract ' . 'types, Union and Interface, provide the Object types possible ' . 'at runtime. List and NonNull types compose other types.', 'fields' => function () { return ['kind' => ['type' => Type::nonNull(self::_typeKind()), 'resolve' => function (Type $type) { switch (true) { case $type instanceof ListOfType: return TypeKind::LIST_KIND; case $type instanceof NonNull: return TypeKind::NON_NULL; case $type instanceof ScalarType: return TypeKind::SCALAR; case $type instanceof ObjectType: return TypeKind::OBJECT; case $type instanceof EnumType: return TypeKind::ENUM; case $type instanceof InputObjectType: return TypeKind::INPUT_OBJECT; case $type instanceof InterfaceType: return TypeKind::INTERFACE_KIND; case $type instanceof UnionType: return TypeKind::UNION; default: throw new \Exception("Unknown kind of type: " . Utils::printSafe($type)); } }], 'name' => ['type' => Type::string()], 'description' => ['type' => Type::string()], 'fields' => ['type' => Type::listOf(Type::nonNull(self::_field())), 'args' => ['includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false]], 'resolve' => function (Type $type, $args) { if ($type instanceof ObjectType || $type instanceof InterfaceType) { $fields = $type->getFields(); if (empty($args['includeDeprecated'])) { $fields = array_filter($fields, function (FieldDefinition $field) { return !$field->deprecationReason; }); } return array_values($fields); } return null; }], 'interfaces' => ['type' => Type::listOf(Type::nonNull(self::_type())), 'resolve' => function ($type) { if ($type instanceof ObjectType) { return $type->getInterfaces(); } return null; }], 'possibleTypes' => ['type' => Type::listOf(Type::nonNull(self::_type())), 'resolve' => function ($type, $args, $context, ResolveInfo $info) { if ($type instanceof InterfaceType || $type instanceof UnionType) { return $info->schema->getPossibleTypes($type); } return null; }], 'enumValues' => ['type' => Type::listOf(Type::nonNull(self::_enumValue())), 'args' => ['includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false]], 'resolve' => function ($type, $args) { if ($type instanceof EnumType) { $values = array_values($type->getValues()); if (empty($args['includeDeprecated'])) { $values = array_filter($values, function ($value) { return !$value->deprecationReason; }); } return $values; } return null; }], 'inputFields' => ['type' => Type::listOf(Type::nonNull(self::_inputValue())), 'resolve' => function ($type) { if ($type instanceof InputObjectType) { return array_values($type->getFields()); } return null; }], 'ofType' => ['type' => self::_type(), 'resolve' => function ($type) { if ($type instanceof WrappingType) { return $type->getWrappedType(); } return null; }]]; }]); } return self::$map['__Type']; }
/** * Produces a GraphQL Value AST given a PHP value. * * Optionally, a GraphQL type may be provided, which will be used to * disambiguate between value primitives. * * | PHP Value | GraphQL Value | * | ------------- | -------------------- | * | Object | Input Object | * | Assoc Array | Input Object | * | Array | List | * | Boolean | Boolean | * | String | String / Enum Value | * | Int | Int | * | Float | Int / Float | * | Mixed | Enum Value | * | null | NullValue | * * @param $value * @param InputType $type * @return ObjectValueNode|ListValueNode|BooleanValueNode|IntValueNode|FloatValueNode|EnumValueNode|StringValueNode|NullValueNode */ static function astFromValue($value, InputType $type) { if ($type instanceof NonNull) { $astValue = self::astFromValue($value, $type->getWrappedType()); if ($astValue instanceof NullValueNode) { return null; } return $astValue; } if ($value === null) { return new NullValueNode([]); } // Convert PHP array to GraphQL list. If the GraphQLType is a list, but // the value is not an array, convert the value using the list's item type. if ($type instanceof ListOfType) { $itemType = $type->getWrappedType(); if (is_array($value) || $value instanceof \Traversable) { $valuesNodes = []; foreach ($value as $item) { $itemNode = self::astFromValue($item, $itemType); if ($itemNode) { $valuesNodes[] = $itemNode; } } return new ListValueNode(['values' => $valuesNodes]); } return self::astFromValue($value, $itemType); } // Populate the fields of the input object by creating ASTs from each value // in the PHP object according to the fields in the input type. if ($type instanceof InputObjectType) { $isArray = is_array($value); $isArrayLike = $isArray || $value instanceof \ArrayAccess; if ($value === null || !$isArrayLike && !is_object($value)) { return null; } $fields = $type->getFields(); $fieldNodes = []; foreach ($fields as $fieldName => $field) { if ($isArrayLike) { $fieldValue = isset($value[$fieldName]) ? $value[$fieldName] : null; } else { $fieldValue = isset($value->{$fieldName}) ? $value->{$fieldName} : null; } // Have to check additionally if key exists, since we differentiate between // "no key" and "value is null": if (null !== $fieldValue) { $fieldExists = true; } else { if ($isArray) { $fieldExists = array_key_exists($fieldName, $value); } else { if ($isArrayLike) { /** @var \ArrayAccess $value */ $fieldExists = $value->offsetExists($fieldName); } else { $fieldExists = property_exists($value, $fieldName); } } } if ($fieldExists) { $fieldNode = self::astFromValue($fieldValue, $field->getType()); if ($fieldNode) { $fieldNodes[] = new ObjectFieldNode(['name' => new NameNode(['value' => $fieldName]), 'value' => $fieldNode]); } } } return new ObjectValueNode(['fields' => $fieldNodes]); } // Since value is an internally represented value, it must be serialized // to an externally represented value before converting into an AST. if ($type instanceof LeafType) { $serialized = $type->serialize($value); } else { throw new InvariantViolation("Must provide Input Type, cannot use: " . Utils::printSafe($type)); } if (null === $serialized) { return null; } // Others serialize based on their corresponding PHP scalar types. if (is_bool($serialized)) { return new BooleanValueNode(['value' => $serialized]); } if (is_int($serialized)) { return new IntValueNode(['value' => $serialized]); } if (is_float($serialized)) { if ((int) $serialized == $serialized) { return new IntValueNode(['value' => $serialized]); } return new FloatValueNode(['value' => $serialized]); } if (is_string($serialized)) { // Enum types use Enum literals. if ($type instanceof EnumType) { return new EnumValueNode(['value' => $serialized]); } // ID types can use Int literals. $asInt = (int) $serialized; if ($type instanceof IDType && (string) $asInt === $serialized) { return new IntValueNode(['value' => $serialized]); } // Use json_encode, which uses the same string encoding as GraphQL, // then remove the quotes. return new StringValueNode(['value' => substr(json_encode($serialized), 1, -1)]); } throw new InvariantViolation('Cannot convert value to AST: ' . Utils::printSafe($serialized)); }
/** * @param array|Config $fields * @param string $parentTypeName * @return array */ public static function createMap(array $fields, $parentTypeName = null) { $map = []; foreach ($fields as $name => $field) { if (is_array($field)) { if (!isset($field['name']) && is_string($name)) { $field['name'] = $name; } $fieldDef = self::create($field, $parentTypeName); } else { if ($field instanceof FieldDefinition) { $fieldDef = $field; } else { if (is_string($name)) { $fieldDef = self::create(['name' => $name, 'type' => $field], $parentTypeName); } else { throw new InvariantViolation("Unexpected field definition for type {$parentTypeName} at key {$name}: " . Utils::printSafe($field)); } } } $map[$fieldDef->name] = $fieldDef; } return $map; }
/** * Offset to set * @link http://php.net/manual/en/arrayaccess.offsetset.php * @param mixed $offset <p> * The offset to assign the value to. * </p> * @param mixed $value <p> * The value to set. * </p> * @return void * @since 5.0.0 */ public function offsetSet($offset, $value) { if (is_scalar($offset)) { $this->scalarStore[$offset] = $value; } else { if (is_object($offset)) { $this->objectStore[$offset] = $value; } else { if (is_array($offset)) { $this->arrayKeys[] = $offset; $this->arrayValues[] = $value; } else { throw new \InvalidArgumentException("Unexpected offset type: " . Utils::printSafe($offset)); } } } }
/** * @return InterfaceType[] */ public function getInterfaces() { if (null === $this->interfaces) { $interfaces = isset($this->config['interfaces']) ? $this->config['interfaces'] : []; $interfaces = is_callable($interfaces) ? call_user_func($interfaces) : $interfaces; $this->interfaces = []; foreach ($interfaces as $iface) { $iface = Type::resolve($iface); if (!$iface instanceof InterfaceType) { throw new InvariantViolation("Expecting interface type, got " . Utils::printSafe($iface)); } $this->interfaces[] = $iface; } } return $this->interfaces; }
/** * Given a PHP value and a GraphQL type, determine if the value will be * accepted for that type. This is primarily useful for validating the * runtime values of query variables. * * @param $value * @param InputType $type * @return array */ private static function isValidPHPValue($value, InputType $type) { // A value must be provided if the type is non-null. if ($type instanceof NonNull) { if (null === $value) { return ['Expected "' . Utils::printSafe($type) . '", found null.']; } return self::isValidPHPValue($value, $type->getWrappedType()); } if (null === $value) { return []; } // Lists accept a non-list value as a list of one. if ($type instanceof ListOfType) { $itemType = $type->getWrappedType(); if (is_array($value)) { $tmp = []; foreach ($value as $index => $item) { $errors = self::isValidPHPValue($item, $itemType); $tmp = array_merge($tmp, Utils::map($errors, function ($error) use($index) { return "In element #{$index}: {$error}"; })); } return $tmp; } return self::isValidPHPValue($value, $itemType); } // Input objects check each defined field. if ($type instanceof InputObjectType) { if (!is_object($value) && !is_array($value)) { return ["Expected \"{$type->name}\", found not an object."]; } $fields = $type->getFields(); $errors = []; // Ensure every provided field is defined. $props = is_object($value) ? get_object_vars($value) : $value; foreach ($props as $providedField => $tmp) { if (!isset($fields[$providedField])) { $errors[] = "In field \"{$providedField}\": Unknown field."; } } // Ensure every defined field is valid. foreach ($fields as $fieldName => $tmp) { $newErrors = self::isValidPHPValue(isset($value[$fieldName]) ? $value[$fieldName] : null, $fields[$fieldName]->getType()); $errors = array_merge($errors, Utils::map($newErrors, 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->parseValue($value); if (null === $parseResult) { $v = json_encode($value); return ["Expected type \"{$type->name}\", found {$v}."]; } return []; } throw new InvariantViolation('Must be input type'); }