Hello,
if PHP is configured via FPM, then it's very hard to spot. Recently, I moved with the application to FrankenPHP (but I bet other worker-alike runtimes will reveal similar behavior). Let's start with a resolve function:
'items' => [
'type' => Type::listOf(Type::string()),
'args' => [
'filter' => [
'type' => $filterType,
'defaultValue' => new Filter(),
],
],
'resolve' => function ($root, array $args): array {
static $counter = 0;
$filter = $args['filter'];
if ($counter++ == 0) {
$args['filter']->status = 'polluted';
}
return [
"status={$filter->status}",
"limit={$filter->limit}",
];
},
],
This is a little bit synthetic proof of concept, but I want to set the status to polluted only for a first response. Other should return a default value that's held as a public property default value of Filter class.
class Filter
{
public function __construct(
public string $status = 'active',
public int $limit = 10,
) {}
}
But:
$schema = new Schema(['query' => $queryType]);
$gql = '{ items }';
echo "=== Request 1: ===" . PHP_EOL;
$result = GraphQL::executeQuery($schema, $gql);
print_r($result->toArray());
echo PHP_EOL . "=== Request 2: ===" . PHP_EOL;
$result = GraphQL::executeQuery($schema, '{ items }');
print_r($result->toArray());
Results in:
=== Request 1: ===
Array
(
[data] => Array
(
[items] => Array
(
[0] => status=polluted
[1] => limit=10
)
)
)
=== Request 2: ===
Array
(
[data] => Array
(
[items] => Array
(
[0] => status=polluted
[1] => limit=10
)
)
)
I dug what's going on and it turns out if the defaultValue is an object, it's always passed as a reference:
The fix is an oneliner (ReferenceExecutor):
$args = $this->fieldArgsCache[$fieldDef][$fieldNode] ??= $argsMapper(Values::getArgumentValues(
$fieldDef,
$fieldNode,
$this->exeContext->variableValues,
$this->exeContext->schema,
), $fieldDef, $fieldNode, $contextValue);
$args = unserialize(serialize($args));
return $resolveFn($rootValue, $args, $contextValue, $info);
And then everything works fine:
If you are fine with a solution with unserialize/serialize, then I'll prepare a pull request with a fix.
Hello,
if PHP is configured via FPM, then it's very hard to spot. Recently, I moved with the application to FrankenPHP (but I bet other worker-alike runtimes will reveal similar behavior). Let's start with a resolve function:
This is a little bit synthetic proof of concept, but I want to set the status to
pollutedonly for a first response. Other should return a default value that's held as a public property default value ofFilterclass.But:
Results in:
I dug what's going on and it turns out if the
defaultValueis an object, it's always passed as a reference:The fix is an oneliner (ReferenceExecutor):
And then everything works fine:
If you are fine with a solution with
unserialize/serialize, then I'll prepare a pull request with a fix.