11<?php
22/**
3- * Copyright © Magento, Inc. All rights reserved.
4- * See COPYING.txt for license details .
3+ * Copyright 2018 Adobe
4+ * All Rights Reserved .
55 */
66declare (strict_types=1 );
77
88namespace Magento \Framework \GraphQl \Query ;
99
1010use GraphQL \Language \AST \DocumentNode ;
11+ use GraphQL \Language \AST \FieldNode ;
1112use GraphQL \Language \AST \NodeKind ;
13+ use GraphQL \Language \AST \SelectionSetNode ;
1214use GraphQL \Language \Visitor ;
1315use GraphQL \Validator \DocumentValidator ;
1416use GraphQL \Validator \Rules \DisableIntrospection ;
@@ -49,6 +51,11 @@ class QueryComplexityLimiter
4951 */
5052 private $ queryParser ;
5153
54+ /**
55+ * @var MaximumAliasConfiguration
56+ */
57+ private $ maximumAliasConfiguration ;
58+
5259 /**
5360 * @var array
5461 */
@@ -61,17 +68,21 @@ class QueryComplexityLimiter
6168 * @param int $queryComplexity
6269 * @param IntrospectionConfiguration $introspectionConfig
6370 * @param QueryParser|null $queryParser
71+ * @param MaximumAliasConfiguration|null $maximumAliasConfiguration
6472 */
6573 public function __construct (
6674 int $ queryDepth ,
6775 int $ queryComplexity ,
6876 IntrospectionConfiguration $ introspectionConfig ,
69- ?QueryParser $ queryParser = null
77+ ?QueryParser $ queryParser = null ,
78+ ?MaximumAliasConfiguration $ maximumAliasConfiguration = null
7079 ) {
7180 $ this ->queryDepth = $ queryDepth ;
7281 $ this ->queryComplexity = $ queryComplexity ;
7382 $ this ->introspectionConfig = $ introspectionConfig ;
7483 $ this ->queryParser = $ queryParser ?: ObjectManager::getInstance ()->get (QueryParser::class);
84+ $ this ->maximumAliasConfiguration = $ maximumAliasConfiguration ?:
85+ ObjectManager::getInstance ()->get (MaximumAliasConfiguration::class);
7586 }
7687
7788 /**
@@ -138,4 +149,64 @@ public function validateFieldCount(DocumentNode|string $query): void
138149 }
139150 }
140151 }
152+
153+ /**
154+ * Performs a preliminary Alias count check before performing more extensive query validation.
155+ *
156+ * This is necessary for performance optimization, as extremely large number of alias in a request
157+ * require a substantial amount of resource can affect server performance.
158+ *
159+ * @param DocumentNode $query
160+ * @return void
161+ * @throws GraphQlInputException
162+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
163+ */
164+ public function validateAliasCount (DocumentNode $ query ): void
165+ {
166+ if ($ this ->maximumAliasConfiguration ->isMaximumAliasLimitEnabled ()) {
167+ $ aliasCount = 0 ;
168+ foreach ($ query ->definitions as $ definition ) {
169+ if (property_exists ($ definition , 'selectionSet ' )) {
170+ $ aliasCount += $ this ->countAliasesInSelectionSet ($ definition ->selectionSet );
171+ }
172+ }
173+ $ allowedAliasCount = $ this ->maximumAliasConfiguration ->getMaximumAliasAllowed ();
174+ if ($ aliasCount > $ allowedAliasCount ) {
175+ throw new GraphQlInputException (__ (
176+ 'Max Aliases in query should be %1 but got %2. ' ,
177+ $ allowedAliasCount ,
178+ $ aliasCount
179+ ));
180+ }
181+ }
182+ }
183+
184+ /**
185+ * Performs counting of aliases in a graphql request
186+ *
187+ * @param ?SelectionSetNode $selectionSet
188+ * @return int
189+ */
190+ private function countAliasesInSelectionSet (?SelectionSetNode $ selectionSet ): int
191+ {
192+ if ($ selectionSet === null ) {
193+ return 0 ;
194+ }
195+
196+ $ aliasCount = 0 ;
197+
198+ foreach ($ selectionSet ->selections as $ selection ) {
199+ if ($ selection instanceof FieldNode) {
200+ if ($ selection ->alias !== null ) {
201+ $ aliasCount ++;
202+ }
203+
204+ if ($ selection ->selectionSet !== null ) {
205+ $ aliasCount += $ this ->countAliasesInSelectionSet ($ selection ->selectionSet );
206+ }
207+ }
208+ }
209+
210+ return $ aliasCount ;
211+ }
141212}
0 commit comments