예제 #1
0
 private function buildFieldArguments(FieldNode $node)
 {
     $rawVariableValues = $this->getRawVariableValues();
     $astFieldInfo = $this->astFieldInfo($node);
     $fieldDef = $astFieldInfo[1];
     $args = [];
     if ($fieldDef instanceof FieldDefinition) {
         $variableValues = Values::getVariableValues($this->context->getSchema(), $this->variableDefs, $rawVariableValues);
         $args = Values::getArgumentValues($fieldDef, $node, $variableValues);
     }
     return $args;
 }
예제 #2
0
 /**
  * Resolves the field on the given source object. In particular, this
  * figures out the value that the field returns by calling its resolve function,
  * then calls completeValue to complete promises, serialize scalars, or execute
  * the sub-selection-set for objects.
  */
 private static function resolveField(ExecutionContext $exeContext, ObjectType $parentType, $source, $fieldASTs, $path)
 {
     $fieldAST = $fieldASTs[0];
     $fieldName = $fieldAST->name->value;
     $fieldDef = self::getFieldDef($exeContext->schema, $parentType, $fieldName);
     if (!$fieldDef) {
         return self::$UNDEFINED;
     }
     $returnType = $fieldDef->getType();
     // Build hash of arguments from the field.arguments AST, using the
     // variables scope to fulfill any variable references.
     $args = Values::getArgumentValues($fieldDef->args, $fieldAST->arguments, $exeContext->variableValues);
     // The resolve function's optional third argument is a collection of
     // information about the current execution state.
     $info = new ResolveInfo(['fieldName' => $fieldName, 'fieldASTs' => $fieldASTs, 'returnType' => $returnType, 'parentType' => $parentType, 'path' => $path, 'schema' => $exeContext->schema, 'fragments' => $exeContext->fragments, 'rootValue' => $exeContext->rootValue, 'operation' => $exeContext->operation, 'variableValues' => $exeContext->variableValues]);
     if (isset($fieldDef->resolveFn)) {
         $resolveFn = $fieldDef->resolveFn;
     } else {
         if (isset($parentType->resolveFieldFn)) {
             $resolveFn = $parentType->resolveFieldFn;
         } else {
             $resolveFn = self::$defaultResolveFn;
         }
     }
     // The resolve function's optional third argument is a context value that
     // is provided to every resolve function within an execution. It is commonly
     // used to represent an authenticated user, or request-specific caches.
     $context = $exeContext->contextValue;
     // Get the resolve function, regardless of if its result is normal
     // or abrupt (error).
     $result = self::resolveOrError($resolveFn, $source, $args, $context, $info);
     $result = self::completeValueCatchingError($exeContext, $returnType, $fieldASTs, $info, $path, $result);
     return $result;
 }
예제 #3
0
 /**
  * Isolates the "ReturnOrAbrupt" behavior to not de-opt the `resolveField`
  * function. Returns the result of resolveFn or the abrupt-return Error object.
  *
  * @param ExecutionContext $exeContext
  * @param FieldDefinition $fieldDef
  * @param FieldNode $fieldNode
  * @param callable $resolveFn
  * @param mixed $source
  * @param mixed $context
  * @param ResolveInfo $info
  * @return \Exception|mixed
  */
 private static function resolveOrError($exeContext, $fieldDef, $fieldNode, $resolveFn, $source, $context, $info)
 {
     try {
         // Build hash of arguments from the field.arguments AST, using the
         // variables scope to fulfill any variable references.
         $args = Values::getArgumentValues($fieldDef, $fieldNode, $exeContext->variableValues);
         return call_user_func($resolveFn, $source, $args, $context, $info);
     } catch (\Exception $error) {
         return $error;
     }
 }
예제 #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);
 }
예제 #5
0
 /**
  * Resolves the field on the given source object. In particular, this
  * figures out the value that the field returns by calling its resolve function,
  * then calls completeValue to complete promises, serialize scalars, or execute
  * the sub-selection-set for objects.
  */
 private static function resolveField(ExecutionContext $exeContext, ObjectType $parentType, $source, $fieldASTs)
 {
     $fieldAST = $fieldASTs[0];
     $fieldName = $fieldAST->name->value;
     $fieldDef = self::getFieldDef($exeContext->schema, $parentType, $fieldName);
     if (!$fieldDef) {
         return self::$UNDEFINED;
     }
     $returnType = $fieldDef->getType();
     if (isset($fieldDef->resolve)) {
         $resolveFn = $fieldDef->resolve;
     } else {
         if (isset(self::$defaultResolveFn)) {
             $resolveFn = self::$defaultResolveFn;
         } else {
             $resolveFn = [__CLASS__, 'defaultResolveFn'];
         }
     }
     // Build hash of arguments from the field.arguments AST, using the
     // variables scope to fulfill any variable references.
     // TODO: find a way to memoize, in case this field is within a List type.
     $args = Values::getArgumentValues($fieldDef->args, $fieldAST->arguments, $exeContext->variableValues);
     // The resolve function's optional third argument is a collection of
     // information about the current execution state.
     $info = new ResolveInfo(['fieldName' => $fieldName, 'fieldASTs' => $fieldASTs, 'returnType' => $returnType, 'parentType' => $parentType, 'schema' => $exeContext->schema, 'fragments' => $exeContext->fragments, 'rootValue' => $exeContext->rootValue, 'operation' => $exeContext->operation, 'variableValues' => $exeContext->variableValues]);
     // If an error occurs while calling the field `resolve` function, ensure that
     // it is wrapped as a GraphQLError with locations. Log this error and return
     // null if allowed, otherwise throw the error so the parent field can handle
     // it.
     try {
         $result = call_user_func($resolveFn, $source, $args, $info);
     } catch (\Exception $error) {
         $reportedError = Error::createLocatedError($error, $fieldASTs);
         if ($returnType instanceof NonNull) {
             throw $reportedError;
         }
         $exeContext->addError($reportedError);
         return null;
     }
     return self::completeValueCatchingError($exeContext, $returnType, $fieldASTs, $info, $result);
 }
 /**
  * Given list of parent type values returns corresponding list of field values
  *
  * In particular, this
  * figures out the value that the field returns by calling its `resolve` or `map` function,
  * then calls `completeValue` on each value to serialize scalars, or execute the sub-selection-set
  * for objects.
  *
  * @param ExecutionContext $exeContext
  * @param ObjectType $parentType
  * @param $sourceValueList
  * @param $fieldASTs
  * @return array
  * @throws Error
  */
 private static function resolveField(ExecutionContext $exeContext, ObjectType $parentType, $sourceValueList, $fieldASTs, $responseName, &$resolveResult)
 {
     $fieldAST = $fieldASTs[0];
     $fieldName = $fieldAST->name->value;
     $fieldDef = self::getFieldDef($exeContext->schema, $parentType, $fieldName);
     if (!$fieldDef) {
         return;
     }
     $returnType = $fieldDef->getType();
     // Build hash of arguments from the field.arguments AST, using the
     // variables scope to fulfill any variable references.
     // TODO: find a way to memoize, in case this field is within a List type.
     $args = Values::getArgumentValues($fieldDef->args, $fieldAST->arguments, $exeContext->variableValues);
     // The resolve function's optional third argument is a collection of
     // information about the current execution state.
     $info = new ResolveInfo(['fieldName' => $fieldName, 'fieldASTs' => $fieldASTs, 'returnType' => $returnType, 'parentType' => $parentType, 'schema' => $exeContext->schema, 'fragments' => $exeContext->fragments, 'rootValue' => $exeContext->rootValue, 'operation' => $exeContext->operation, 'variableValues' => $exeContext->variableValues]);
     $mapFn = $fieldDef->mapFn;
     // If an error occurs while calling the field `map` or `resolve` function, ensure that
     // it is wrapped as a GraphQLError with locations. Log this error and return
     // null if allowed, otherwise throw the error so the parent field can handle
     // it.
     if ($mapFn) {
         try {
             $mapped = call_user_func($mapFn, $sourceValueList, $args, $info);
             $validType = is_array($mapped) || $mapped instanceof \Traversable && $mapped instanceof \Countable;
             $mappedCount = count($mapped);
             $sourceCount = count($sourceValueList);
             Utils::invariant($validType && count($mapped) === count($sourceValueList), "Function `map` of {$parentType}.{$fieldName} is expected to return array or " . "countable traversable with exact same number of items as list being mapped. " . "Got '%s' with count '{$mappedCount}' against '{$sourceCount}' expected.", Utils::getVariableType($mapped));
         } catch (\Exception $error) {
             $reportedError = Error::createLocatedError($error, $fieldASTs);
             if ($returnType instanceof NonNull) {
                 throw $reportedError;
             }
             $exeContext->addError($reportedError);
             return null;
         }
         foreach ($mapped as $index => $value) {
             $resolveResult[$index][$responseName] = self::completeValueCatchingError($exeContext, $returnType, $fieldASTs, $info, $value);
         }
     } else {
         if (isset($fieldDef->resolveFn)) {
             $resolveFn = $fieldDef->resolveFn;
         } else {
             if (isset($parentType->resolveFieldFn)) {
                 $resolveFn = $parentType->resolveFieldFn;
             } else {
                 $resolveFn = self::$defaultResolveFn;
             }
         }
         foreach ($sourceValueList as $index => $value) {
             try {
                 $resolved = call_user_func($resolveFn, $value, $args, $info);
             } catch (\Exception $error) {
                 $reportedError = Error::createLocatedError($error, $fieldASTs);
                 if ($returnType instanceof NonNull) {
                     throw $reportedError;
                 }
                 $exeContext->addError($reportedError);
                 $resolved = null;
             }
             $resolveResult[$index][$responseName] = self::completeValueCatchingError($exeContext, $returnType, $fieldASTs, $info, $resolved);
         }
     }
 }
예제 #7
0
 /**
  * Given list of parent type values returns corresponding list of field values
  *
  * In particular, this
  * figures out the value that the field returns by calling its `resolve` or `map` function,
  * then calls `completeValue` on each value to serialize scalars, or execute the sub-selection-set
  * for objects.
  *
  * @param ExecutionContext $exeContext
  * @param ObjectType $parentType
  * @param $sourceValueList
  * @param $fieldASTs
  * @return array
  * @throws Error
  */
 private static function resolveField(ExecutionContext $exeContext, ObjectType $parentType, $sourceValueList, $fieldASTs, $responseName, &$resolveResult)
 {
     $fieldAST = $fieldASTs[0];
     $uid = self::getUid($fieldAST);
     // Get memoized variables if they exist
     if (isset(self::$memoized['resolveField'][$uid])) {
         $memoized = self::$memoized['resolveField'][$uid];
         $fieldDef = $memoized['fieldDef'];
         $returnType = $fieldDef->getType();
         $args = $memoized['args'];
         $info = $memoized['info'];
     } else {
         $fieldName = $fieldAST->name->value;
         $fieldDef = self::getFieldDef($exeContext->schema, $parentType, $fieldName);
         if (!$fieldDef) {
             return;
         }
         $returnType = $fieldDef->getType();
         // Build hash of arguments from the field.arguments AST, using the
         // variables scope to fulfill any variable references.
         $args = Values::getArgumentValues($fieldDef->args, $fieldAST->arguments, $exeContext->variableValues);
         // The resolve function's optional third argument is a collection of
         // information about the current execution state.
         $info = new ResolveInfo(['fieldName' => $fieldName, 'fieldASTs' => $fieldASTs, 'returnType' => $returnType, 'parentType' => $parentType, 'schema' => $exeContext->schema, 'fragments' => $exeContext->fragments, 'rootValue' => $exeContext->rootValue, 'operation' => $exeContext->operation, 'variableValues' => $exeContext->variableValues]);
         self::$memoized['resolveField'][$uid] = ['fieldDef' => $fieldDef, 'args' => $args, 'info' => $info, 'results' => new \SplObjectStorage()];
     }
     $mapFn = $fieldDef->mapFn;
     // If an error occurs while calling the field `map` or `resolve` function, ensure that
     // it is wrapped as a GraphQLError with locations. Log this error and return
     // null if allowed, otherwise throw the error so the parent field can handle
     // it.
     if ($mapFn) {
         try {
             $mapped = call_user_func($mapFn, $sourceValueList, $args, $info);
             Utils::invariant(is_array($mapped) && count($mapped) === count($sourceValueList), "Function `map` of {$parentType}.{$fieldName} is expected to return array " . "with exact same number of items as list being mapped (first argument of `map`)");
         } catch (\Exception $error) {
             $reportedError = Error::createLocatedError($error, $fieldASTs);
             if ($returnType instanceof NonNull) {
                 throw $reportedError;
             }
             $exeContext->addError($reportedError);
             return null;
         }
         foreach ($mapped as $index => $value) {
             $resolveResult[$index][$responseName] = self::completeValueCatchingError($exeContext, $returnType, $fieldASTs, $info, $value);
         }
     } else {
         if (isset($fieldDef->resolveFn)) {
             $resolveFn = $fieldDef->resolveFn;
         } else {
             if (isset($parentType->resolveFieldFn)) {
                 $resolveFn = $parentType->resolveFieldFn;
             } else {
                 $resolveFn = self::$defaultResolveFn;
             }
         }
         foreach ($sourceValueList as $index => $value) {
             // If FieldAST uid and parent object are both the same as in a previous run,
             // use its result instead to prevent unnecessary work. Works for objects only.
             $isObject = is_object($value);
             if ($isObject && isset(self::$memoized['resolveField'][$uid]['results'][$value])) {
                 $result = self::$memoized['resolveField'][$uid]['results'][$value];
             } else {
                 try {
                     $resolved = call_user_func($resolveFn, $value, $args, $info);
                 } catch (\Exception $error) {
                     $reportedError = Error::createLocatedError($error, $fieldASTs);
                     if ($returnType instanceof NonNull) {
                         throw $reportedError;
                     }
                     $exeContext->addError($reportedError);
                     $resolved = null;
                 }
                 $result = self::completeValueCatchingError($exeContext, $returnType, $fieldASTs, $info, $resolved);
                 if ($isObject) {
                     self::$memoized['resolveField'][$uid]['results'][$value] = $result;
                 }
             }
             $resolveResult[$index][$responseName] = $result;
         }
     }
 }
예제 #8
0
 /**
  * Resolves the field on the given source object. In particular, this
  * figures out the value that the field returns by calling its resolve function,
  * then calls completeValue to complete promises, serialize scalars, or execute
  * the sub-selection-set for objects.
  */
 private static function resolveField(ExecutionContext $exeContext, ObjectType $parentType, $source, $fieldASTs)
 {
     $fieldAST = $fieldASTs[0];
     $uid = self::getFieldUid($fieldAST, $parentType);
     // Get memoized variables if they exist
     if (isset($exeContext->memoized['resolveField'][$uid])) {
         $memoized = $exeContext->memoized['resolveField'][$uid];
         $fieldDef = $memoized['fieldDef'];
         $returnType = $fieldDef->getType();
         $args = $memoized['args'];
         $info = $memoized['info'];
     } else {
         $fieldName = $fieldAST->name->value;
         $fieldDef = self::getFieldDef($exeContext->schema, $parentType, $fieldName);
         if (!$fieldDef) {
             return self::$UNDEFINED;
         }
         $returnType = $fieldDef->getType();
         // Build hash of arguments from the field.arguments AST, using the
         // variables scope to fulfill any variable references.
         $args = Values::getArgumentValues($fieldDef->args, $fieldAST->arguments, $exeContext->variableValues);
         // The resolve function's optional third argument is a collection of
         // information about the current execution state.
         $info = new ResolveInfo(['fieldName' => $fieldName, 'fieldASTs' => $fieldASTs, 'returnType' => $returnType, 'parentType' => $parentType, 'schema' => $exeContext->schema, 'fragments' => $exeContext->fragments, 'rootValue' => $exeContext->rootValue, 'operation' => $exeContext->operation, 'variableValues' => $exeContext->variableValues]);
         // Memoizing results for same query field
         // (useful for lists when several values are resolved against the same field)
         $exeContext->memoized['resolveField'][$uid] = $memoized = ['fieldDef' => $fieldDef, 'args' => $args, 'info' => $info, 'results' => new \SplObjectStorage()];
     }
     // When source value is object it is possible to memoize certain subset of results
     $isObject = is_object($source);
     if ($isObject && isset($memoized['results'][$source])) {
         $result = $memoized['results'][$source];
     } else {
         if (isset($fieldDef->resolveFn)) {
             $resolveFn = $fieldDef->resolveFn;
         } else {
             if (isset($parentType->resolveFieldFn)) {
                 $resolveFn = $parentType->resolveFieldFn;
             } else {
                 $resolveFn = self::$defaultResolveFn;
             }
         }
         // Get the resolve function, regardless of if its result is normal
         // or abrupt (error).
         $result = self::resolveOrError($resolveFn, $source, $args, $info);
         $result = self::completeValueCatchingError($exeContext, $returnType, $fieldASTs, $info, $result);
         if ($isObject) {
             $memoized['results'][$source] = $result;
         }
     }
     return $result;
 }
예제 #9
0
파일: Values.php 프로젝트: aeshion/ZeroPHP
 /**
  * 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');
 }
예제 #10
0
 /**
  * Resolves the field on the given source object. In particular, this
  * figures out the object that the field returns using the resolve function,
  * then calls completeField to coerce scalars or execute the sub
  * selection set for objects.
  */
 private static function resolveFieldOrError(ExecutionContext $exeContext, ObjectType $parentType, $source, $fieldASTs, FieldDefinition $fieldDef)
 {
     $fieldAST = $fieldASTs[0];
     $fieldType = $fieldDef->getType();
     $resolveFn = $fieldDef->resolve ?: [__CLASS__, 'defaultResolveFn'];
     // Build a JS object of arguments from the field.arguments AST, using the
     // variables scope to fulfill any variable references.
     // TODO: find a way to memoize, in case this field is within a Array type.
     $args = Values::getArgumentValues($fieldDef->args, $fieldAST->arguments, $exeContext->variables);
     try {
         $result = call_user_func($resolveFn, $source, $args, $exeContext->root, $fieldAST, $fieldType, $parentType, $exeContext->schema);
     } catch (\Exception $error) {
         throw new Error($error->getMessage(), [$fieldAST], $error->getTrace());
     }
     return self::completeField($exeContext, $fieldType, $fieldASTs, $result);
 }