diff --git a/src/Support/AliasArguments/AliasArguments.php b/src/Support/AliasArguments/AliasArguments.php index a38b71c3..8c978cd2 100644 --- a/src/Support/AliasArguments/AliasArguments.php +++ b/src/Support/AliasArguments/AliasArguments.php @@ -32,7 +32,7 @@ public function __construct(array $queryArguments, array $requestArguments) public function get(): array { - $pathsWithAlias = $this->getAliasesInFields($this->queryArguments); + $pathsWithAlias = $this->getAliasesInFields($this->queryArguments, $this->requestArguments); return (new ArrayKeyChange())->modify($this->requestArguments, $pathsWithAlias); } @@ -59,7 +59,20 @@ protected function getArrayDepth(array $array): int return $maxDepth; } - protected function getAliasesInFields(array $fields, $prefix = ''): array + /** + * Get aliases from fields, only traversing fields present in request data. + * + * This prevents exponential time complexity with circular type references by only + * exploring the actual data structure sent by the client, not all possible fields + * in the type schema. + * + * @param array $fields Type field definitions + * @param array|null $requestData Actual request data at this level (null for initial call) + * @param string $prefix Path prefix for nested fields + * + * @return array Map of field paths to their aliases + */ + protected function getAliasesInFields(array $fields, ?array $requestData = null, string $prefix = ''): array { // checks for traversal beyond the max depth // this scenario occurs in types with recursive relations @@ -69,6 +82,12 @@ protected function getAliasesInFields(array $fields, $prefix = ''): array $pathAndAlias = []; foreach ($fields as $name => $arg) { + // KEY FIX: Skip fields not present in actual request data + // This prevents exponential explosion with circular type references + if ($requestData !== null && ! array_key_exists($name, $requestData)) { + continue; + } + $type = null; // $arg is either an array DSL notation or an InputObjectField @@ -83,7 +102,7 @@ protected function getAliasesInFields(array $fields, $prefix = ''): array continue; } - $newPrefix = $prefix ? $prefix . '.' . $name : $name; + $newPrefix = $prefix ? $prefix.'.'.$name : $name; $alias = $arg->config['alias'] ?? $arg->alias ?? null; @@ -91,17 +110,39 @@ protected function getAliasesInFields(array $fields, $prefix = ''): array $pathAndAlias[$newPrefix] = $alias; } - if ($this->isWrappedInList($type)) { + $isWrappedInList = $this->isWrappedInList($type); + if ($isWrappedInList) { $newPrefix .= '.*'; } $type = $this->getWrappedType($type); - if (!($type instanceof InputObjectType)) { + if (! ($type instanceof InputObjectType)) { continue; } - $pathAndAlias = $pathAndAlias + $this->getAliasesInFields($type->getFields(), $newPrefix); + // Get the actual data at this field (if requestData provided) + $fieldData = $requestData !== null ? ($requestData[$name] ?? null) : null; + + // If it's a list, process each item + if ($isWrappedInList && is_array($fieldData)) { + foreach ($fieldData as $item) { + if (is_array($item)) { + $pathAndAlias = $pathAndAlias + $this->getAliasesInFields( + $type->getFields(), + $item, + $newPrefix + ); + } + } + } elseif ($fieldData !== null && is_array($fieldData)) { + // Single object + $pathAndAlias = $pathAndAlias + $this->getAliasesInFields( + $type->getFields(), + $fieldData, + $newPrefix + ); + } } return $pathAndAlias;