Namely, schema of the type system that is currently executing, and the fragments defined in the query document
 /**
  * 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);
         }
     }
 }
Exemple #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)
 {
     $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];
     $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;
         }
     }
 }