-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathComplexity.php
More file actions
143 lines (125 loc) · 4.12 KB
/
Copy pathComplexity.php
File metadata and controls
143 lines (125 loc) · 4.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<?php
/**
* GraphQL Query Complexity Calculator.
*
* @package WPGraphQL\Debug\Analysis\Metrics
*/
declare(strict_types=1);
namespace WPGraphQL\Debug\Analysis\Metrics;
use GraphQL\Language\AST\BooleanValueNode;
use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\FragmentSpreadNode;
use GraphQL\Language\AST\InlineFragmentNode;
use GraphQL\Language\AST\VariableNode;
use GraphQL\Language\Parser;
use GraphQL\Language\Visitor;
use GraphQL\Type\Schema;
use GraphQL\Error\SyntaxError;
/**
* Class Complexity
*
* Calculates the complexity of a GraphQL query based on field, fragment, and inline fragment counts,
* with optional consideration for @skip and @include directives.
*/
class Complexity {
/**
* Calculates the estimated complexity of a GraphQL query.
*
* This calculation counts each Field, FragmentSpread, and InlineFragment as 1 unit of complexity.
* It also attempts to respect @skip and @include directives based on provided variables.
*
* @param string $query The GraphQL query string.
* @param array $variables Optional: Variables provided with the query, used for directive evaluation.
* @param Schema|null $schema Optional: The GraphQL schema, useful if advanced directive resolution (beyond simple boolean/variable check) is needed.
* @return array The calculated complexity.
* @throws SyntaxError If the query string is invalid and cannot be parsed.
*/
public function calculate( string $query, array $variables = [], ?Schema $schema = null ): array {
try {
$ast = Parser::parse( $query );
} catch (SyntaxError $error) {
throw $error;
}
$complexity = 0;
Visitor::visit(
$ast,
[
'enter' => function ($node) use (&$complexity, $variables, $schema) {
// Count Field nodes
if ( $node instanceof FieldNode ) {
$include = true;
// Handle @skip and @include directives
if ( ! empty( $node->directives ) ) {
foreach ( $node->directives as $directive ) {
$name = $directive->name->value;
$ifArg = null;
foreach ( $directive->arguments as $arg ) {
if ( 'if' === $arg->name->value ) {
$ifArg = $arg->value;
break;
}
}
$ifValue = true; // Default behavior if 'if' argument is missing or not a boolean/variable.
if ( $ifArg instanceof VariableNode ) {
$varName = $ifArg->name->value;
$ifValue = $variables[ $varName ] ?? true;
} elseif ( $ifArg instanceof BooleanValueNode ) {
// Use the boolean literal value
$ifValue = $ifArg->value;
}
if ( 'skip' === $name && true === $ifValue ) {
$include = false;
break;
}
if ( 'include' === $name && false === $ifValue ) {
$include = false;
break;
}
}
}
if ( $include ) {
$complexity += 1;
}
} elseif ( $node instanceof FragmentSpreadNode ) {
// Count FragmentSpread nodes
$complexity += 1;
} elseif ( $node instanceof InlineFragmentNode ) {
// Count InlineFragment nodes
$complexity += 1;
}
},
]
);
$value = $complexity;
return [
'value' => $value,
'note' => $this->getComplexityNote( $value ),
];
}
/**
* Determines the descriptive note for the complexity value based on predefined ranges.
*
* @param int|null $complexityValue The calculated complexity value.
* @return string The descriptive note.
*/
private function getComplexityNote( ?int $complexityValue ): string {
if ( ! is_numeric( $complexityValue ) ) {
return 'Complexity could not be determined.';
}
if ( $complexityValue <= 20 ) {
return 'Low complexity, excellent for performance.';
} elseif ( $complexityValue <= 50 ) {
return 'Moderate complexity, generally good for most applications.';
} elseif ( $complexityValue <= 100 ) {
return 'High complexity, consider optimizing larger queries for better performance.';
} else {
return 'Very high complexity, significant optimization is highly recommended to prevent performance issues.';
}
}
/**
* @inheritDoc
*/
public function getKey(): string {
return 'complexity';
}
}