Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions docs/class-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ static function executeQuery(
?array $variableValues = null,
?string $operationName = null,
?callable $fieldResolver = null,
?array $validationRules = null
?array $validationRules = null,
bool $trustResult = false
): GraphQL\Executor\ExecutionResult
```

Expand Down Expand Up @@ -100,7 +101,8 @@ static function promiseToExecute(
?array $variableValues = null,
?string $operationName = null,
?callable $fieldResolver = null,
?array $validationRules = null
?array $validationRules = null,
bool $trustResult = false
): GraphQL\Executor\Promise\Promise
```

Expand Down Expand Up @@ -1590,7 +1592,7 @@ Implements the "Evaluating requests" section of the GraphQL specification.
```php
@phpstan-type ArgsMapper callable(array<string, mixed>, FieldDefinition, FieldNode, mixed): mixed
@phpstan-type FieldResolver callable(mixed, array<string, mixed>, mixed, ResolveInfo): mixed
@phpstan-type ImplementationFactory callable(PromiseAdapter, Schema, DocumentNode, mixed, mixed, array<mixed>, ?string, callable, callable): ExecutorImplementation
@phpstan-type ImplementationFactory callable(PromiseAdapter, Schema, DocumentNode, mixed, mixed, array<mixed>, ?string, callable, ?callable, bool): ExecutorImplementation
```

@see \GraphQL\Tests\Executor\ExecutorTest
Expand Down Expand Up @@ -1621,7 +1623,8 @@ static function execute(
$contextValue = null,
?array $variableValues = null,
?string $operationName = null,
?callable $fieldResolver = null
?callable $fieldResolver = null,
bool $trustResult = false
): GraphQL\Executor\ExecutionResult
```

Expand Down Expand Up @@ -1650,7 +1653,8 @@ static function promiseToExecute(
?array $variableValues = null,
?string $operationName = null,
?callable $fieldResolver = null,
?callable $argsMapper = null
?callable $argsMapper = null,
bool $trustResult = false
): GraphQL\Executor\Promise\Promise
```

Expand Down
6 changes: 5 additions & 1 deletion src/Executor/ExecutionContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class ExecutionContext

public PromiseAdapter $promiseAdapter;

public bool $trustResult;

/**
* @param array<string, FragmentDefinitionNode> $fragments
* @param mixed $rootValue
Expand All @@ -73,7 +75,8 @@ public function __construct(
array $errors,
callable $fieldResolver,
callable $argsMapper,
PromiseAdapter $promiseAdapter
PromiseAdapter $promiseAdapter,
bool $trustResult = false
) {
$this->schema = $schema;
$this->fragments = $fragments;
Expand All @@ -85,6 +88,7 @@ public function __construct(
$this->fieldResolver = $fieldResolver;
$this->argsMapper = $argsMapper;
$this->promiseAdapter = $promiseAdapter;
$this->trustResult = $trustResult;
}

public function addError(Error $error): void
Expand Down
13 changes: 9 additions & 4 deletions src/Executor/Executor.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*
* @phpstan-type ArgsMapper callable(array<string, mixed>, FieldDefinition, FieldNode, mixed): mixed
* @phpstan-type FieldResolver callable(mixed, array<string, mixed>, mixed, ResolveInfo): mixed
* @phpstan-type ImplementationFactory callable(PromiseAdapter, Schema, DocumentNode, mixed, mixed, array<mixed>, ?string, callable, callable): ExecutorImplementation
* @phpstan-type ImplementationFactory callable(PromiseAdapter, Schema, DocumentNode, mixed, mixed, array<mixed>, ?string, callable, ?callable, bool): ExecutorImplementation
*
* @see \GraphQL\Tests\Executor\ExecutorTest
*/
Expand Down Expand Up @@ -125,7 +125,8 @@ public static function execute(
$contextValue = null,
?array $variableValues = null,
?string $operationName = null,
?callable $fieldResolver = null
?callable $fieldResolver = null,
bool $trustResult = false
): ExecutionResult {
$promiseAdapter = new SyncPromiseAdapter();

Expand All @@ -137,7 +138,9 @@ public static function execute(
$contextValue,
$variableValues,
$operationName,
$fieldResolver
$fieldResolver,
null,
$trustResult
);

return $promiseAdapter->wait($result);
Expand Down Expand Up @@ -167,7 +170,8 @@ public static function promiseToExecute(
?array $variableValues = null,
?string $operationName = null,
?callable $fieldResolver = null,
?callable $argsMapper = null
?callable $argsMapper = null,
bool $trustResult = false
): Promise {
$executor = (self::$implementationFactory)(
$promiseAdapter,
Expand All @@ -179,6 +183,7 @@ public static function promiseToExecute(
$operationName,
$fieldResolver ?? self::$defaultFieldResolver,
$argsMapper ?? self::$defaultArgsMapper,
$trustResult,
);
Comment thread
mahmudsudo marked this conversation as resolved.

return $executor->doExecute();
Expand Down
70 changes: 40 additions & 30 deletions src/Executor/ReferenceExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ public static function create(
array $variableValues,
?string $operationName,
callable $fieldResolver,
?callable $argsMapper = null // TODO make non-optional in next major release
?callable $argsMapper = null, // TODO make non-optional in next major release
bool $trustResult = false
): ExecutorImplementation {
$exeContext = static::buildExecutionContext(
$schema,
Expand All @@ -117,6 +118,7 @@ public static function create(
$fieldResolver,
$argsMapper ?? Executor::getDefaultArgsMapper(),
$promiseAdapter,
$trustResult
);

if (is_array($exeContext)) {
Expand Down Expand Up @@ -152,7 +154,8 @@ protected static function buildExecutionContext(
?string $operationName,
callable $fieldResolver,
callable $argsMapper,
PromiseAdapter $promiseAdapter
PromiseAdapter $promiseAdapter,
bool $trustResult = false
) {
/** @var list<Error> $errors */
$errors = [];
Expand Down Expand Up @@ -229,7 +232,8 @@ protected static function buildExecutionContext(
$errors,
$fieldResolver,
$argsMapper,
$promiseAdapter
$promiseAdapter,
$trustResult
);
}

Expand Down Expand Up @@ -884,7 +888,7 @@ protected function completeValue(
$result,
$contextValue
);
if ($completed === null) {
if ($completed === null && ! $this->exeContext->trustResult) {
throw new InvariantViolation("Cannot return null for non-nullable field \"{$info->parentType}.{$info->fieldName}\".");
}
Comment on lines +896 to 898

Expand All @@ -897,7 +901,7 @@ protected function completeValue(

// If field type is List, complete each item in the list with the inner type
if ($returnType instanceof ListOfType) {
if (! is_iterable($result)) {
if (! $this->exeContext->trustResult && ! is_iterable($result)) {
$resultType = gettype($result);

throw new InvariantViolation("Expected field {$info->parentType}.{$info->fieldName} to return iterable, but got: {$resultType}.");
Comment thread
mahmudsudo marked this conversation as resolved.
Expand Down Expand Up @@ -1053,6 +1057,10 @@ protected function completeListValue(
*/
protected function completeLeafValue(LeafType $returnType, $result)
{
if ($this->exeContext->trustResult) {
return $result;
}

try {
return $returnType->serialize($result);
} catch (\Throwable $error) {
Expand Down Expand Up @@ -1225,36 +1233,38 @@ protected function completeObjectValue(
// 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.
$isTypeOf = $returnType->isTypeOf($result, $contextValue, $info);
if ($isTypeOf !== null) {
$promise = $this->getPromise($isTypeOf);
if ($promise !== null) {
return $promise->then(function ($isTypeOfResult) use (
$contextValue,
$returnType,
$fieldNodes,
$path,
$unaliasedPath,
$result
) {
if (! $isTypeOfResult) {
throw $this->invalidReturnTypeError($returnType, $result, $fieldNodes);
}

return $this->collectAndExecuteSubfields(
if (! $this->exeContext->trustResult) {
$isTypeOf = $returnType->isTypeOf($result, $contextValue, $info);
if ($isTypeOf !== null) {
$promise = $this->getPromise($isTypeOf);
if ($promise !== null) {
return $promise->then(function ($isTypeOfResult) use (
$contextValue,
$returnType,
$fieldNodes,
$path,
$unaliasedPath,
$result,
$contextValue
);
});
}
$result
) {
if (! $isTypeOfResult) {
throw $this->invalidReturnTypeError($returnType, $result, $fieldNodes);
}

assert(is_bool($isTypeOf), 'Promise would return early');
if (! $isTypeOf) {
throw $this->invalidReturnTypeError($returnType, $result, $fieldNodes);
return $this->collectAndExecuteSubfields(
$returnType,
$fieldNodes,
$path,
$unaliasedPath,
$result,
$contextValue
);
});
}

assert(is_bool($isTypeOf), 'Promise would return early');
if (! $isTypeOf) {
throw $this->invalidReturnTypeError($returnType, $result, $fieldNodes);
}
}
}

Expand Down
13 changes: 9 additions & 4 deletions src/GraphQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ public static function executeQuery(
?array $variableValues = null,
?string $operationName = null,
?callable $fieldResolver = null,
?array $validationRules = null
?array $validationRules = null,
bool $trustResult = false
): ExecutionResult {
Comment thread
mahmudsudo marked this conversation as resolved.
$promiseAdapter = new SyncPromiseAdapter();

Expand All @@ -103,7 +104,8 @@ public static function executeQuery(
$variableValues,
$operationName,
$fieldResolver,
$validationRules
$validationRules,
$trustResult
);

return $promiseAdapter->wait($promise);
Expand Down Expand Up @@ -132,7 +134,8 @@ public static function promiseToExecute(
?array $variableValues = null,
?string $operationName = null,
?callable $fieldResolver = null,
?array $validationRules = null
?array $validationRules = null,
bool $trustResult = false
): Promise {
Comment thread
mahmudsudo marked this conversation as resolved.
try {
$documentNode = $source instanceof DocumentNode
Expand Down Expand Up @@ -168,7 +171,9 @@ public static function promiseToExecute(
$context,
$variableValues,
$operationName,
$fieldResolver
$fieldResolver,
null,
$trustResult
);
Comment thread
mahmudsudo marked this conversation as resolved.
} catch (Error $e) {
return $promiseAdapter->createFulfilled(
Expand Down
3 changes: 2 additions & 1 deletion src/Server/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,8 @@ protected function promiseToExecuteOperation(
$op->variables,
$op->operation,
$config->getFieldResolver(),
$this->resolveValidationRules($config, $op, $doc, $operationType)
$this->resolveValidationRules($config, $op, $doc, $operationType),
$config->getTrustResult()
);
} catch (RequestError $e) {
$result = $promiseAdapter->createFulfilled(
Expand Down
18 changes: 18 additions & 0 deletions src/Server/ServerConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ public static function create(array $config = []): self
case 'promiseAdapter':
$instance->setPromiseAdapter($value);
break;
case 'trustResult':
$instance->setTrustResult($value);
break;
default:
throw new InvariantViolation("Unknown server config option: {$key}");
}
Expand Down Expand Up @@ -135,6 +138,8 @@ public static function create(array $config = []): self

private ?PromiseAdapter $promiseAdapter = null;

private bool $trustResult = false;

/**
* @var callable|null
*
Expand Down Expand Up @@ -276,6 +281,14 @@ public function setPromiseAdapter(PromiseAdapter $promiseAdapter): self
return $this;
}

/** @api */
public function setTrustResult(bool $trustResult): self
{
$this->trustResult = $trustResult;

return $this;
}

/** @return mixed|callable */
public function getContext()
{
Expand Down Expand Up @@ -344,4 +357,9 @@ public function getQueryBatching(): bool
{
return $this->queryBatching;
}

public function getTrustResult(): bool
{
return $this->trustResult;
}
}
Loading
Loading