Skip to content

Commit cedab08

Browse files
authored
Run CI on PHP 8.2 (#1252)
1 parent b868a6f commit cedab08

File tree

12 files changed

+80
-47
lines changed

12 files changed

+80
-47
lines changed

.github/workflows/static-analysis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ jobs:
1717
- 7.4
1818
- 8.0
1919
- 8.1
20+
- 8.2
2021

2122
steps:
2223
- name: Checkout code

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
- 7.4
2020
- 8.0
2121
- 8.1
22+
- 8.2
2223
dependencies:
2324
- highest
2425
include:

phpstan.neon.dist

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,31 @@ parameters:
3333
path: src/Utils/Utils.php
3434
count: 2
3535

36-
# Those come from graphql-php\tests\Language\VisitorTest.php
37-
- "~Access to an undefined property GraphQL\\\\Language\\\\AST\\\\.+::\\$didEnter~"
38-
- "~Access to an undefined property GraphQL\\\\Language\\\\AST\\\\.+::\\$didLeave~"
39-
- "~Access to an undefined property GraphQL\\\\Language\\\\AST\\\\Node::\\$value~"
36+
# In PHP 8.2, PHPStan no longer believes @property works without magic methods,
37+
# but all implementors of the interfaces actually have those properties.
38+
-
39+
message: "~Access to an undefined property .*GraphQL\\\\Type\\\\Definition\\\\NamedType.*::\\$name~"
40+
reportUnmatched: false
41+
-
42+
message: "~Access to an undefined property .*GraphQL\\\\Type\\\\Definition\\\\NamedType.*::\\$description~"
43+
reportUnmatched: false
44+
-
45+
message: "~Access to an undefined property .*GraphQL\\\\Type\\\\Definition\\\\NamedType.*::\\$astNode~"
46+
reportUnmatched: false
47+
-
48+
message: "~Access to an undefined property .*GraphQL\\\\Type\\\\Definition\\\\NamedType.*::\\$extensionASTNodes~"
49+
reportUnmatched: false
50+
-
51+
message: "~Access to an undefined property .*GraphQL\\\\Language\\\\AST\\\\TypeDefinitionNode.*::\\$name~"
52+
reportUnmatched: false
53+
-
54+
message: "~Access to an undefined property .*GraphQL\\\\Language\\\\AST\\\\TypeExtensionNode.*::\\$name~"
55+
reportUnmatched: false
56+
57+
# In PHP 8.2, PHPStan seems to not allow dynamic checks for properties on classes, even when they are not final.
58+
-
59+
message: "~Call to function property_exists\\(\\) with .* will always evaluate to false\\.~"
60+
reportUnmatched: false
4061

4162
# PHPStan does not play nicely with markTestSkipped()
4263
- message: "~Unreachable statement - code above always terminates~"

src/Type/Schema.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ public function getTypeMap(): array
124124
$type = self::resolveType($typeOrLazyType);
125125
assert($type instanceof NamedType);
126126

127+
/** @var string $typeName Necessary assertion for PHPStan + PHP 8.2 */
127128
$typeName = $type->name;
128129
assert(
129130
! isset($this->resolvedTypes[$typeName]) || $type === $this->resolvedTypes[$typeName],

src/Utils/BuildSchema.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,14 @@ public function buildSchema(): Schema
153153
$schemaDef = $definition;
154154
break;
155155
case $definition instanceof TypeDefinitionNode:
156-
$typeDefinitionsMap[$definition->name->value] = $definition;
156+
/** @var string $name Necessary assertion for PHPStan + PHP 8.2 */
157+
$name = $definition->name->value;
158+
$typeDefinitionsMap[$name] = $definition;
157159
break;
158160
case $definition instanceof TypeExtensionNode:
159-
$typeExtensionsMap[$definition->name->value][] = $definition;
161+
/** @var string $name Necessary assertion for PHPStan + PHP 8.2 */
162+
$name = $definition->name->value;
163+
$typeExtensionsMap[$name][] = $definition;
160164
break;
161165
case $definition instanceof DirectiveDefinitionNode:
162166
$directiveDefs[] = $definition;

src/Utils/SchemaExtender.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,13 @@ protected function doExtend(
104104
} elseif ($def instanceof SchemaExtensionNode) {
105105
$schemaExtensions[] = $def;
106106
} elseif ($def instanceof TypeDefinitionNode) {
107-
$typeDefinitionMap[$def->name->value] = $def;
107+
/** @var string $name Necessary assertion for PHPStan + PHP 8.2 */
108+
$name = $def->name->value;
109+
$typeDefinitionMap[$name] = $def;
108110
} elseif ($def instanceof TypeExtensionNode) {
109-
$this->typeExtensionsMap[$def->name->value][] = $def;
111+
/** @var string $name Necessary assertion for PHPStan + PHP 8.2 */
112+
$name = $def->name->value;
113+
$this->typeExtensionsMap[$name][] = $def;
110114
} elseif ($def instanceof DirectiveDefinitionNode) {
111115
$directiveDefinitions[] = $def;
112116
}

src/Validator/Rules/PossibleTypeExtensions.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ public function getSDLVisitor(SDLValidationContext $context): array
3333
$definedTypes = [];
3434
foreach ($context->getDocument()->definitions as $def) {
3535
if ($def instanceof TypeDefinitionNode) {
36-
$definedTypes[$def->name->value] = $def;
36+
/** @var string $name Necessary assertion for PHPStan + PHP 8.2 */
37+
$name = $def->name->value;
38+
$definedTypes[$name] = $def;
3739
}
3840
}
3941

src/Validator/Rules/QueryDepth.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use GraphQL\Error\Error;
66
use GraphQL\Language\AST\FieldNode;
7+
use GraphQL\Language\AST\FragmentDefinitionNode;
78
use GraphQL\Language\AST\FragmentSpreadNode;
89
use GraphQL\Language\AST\InlineFragmentNode;
910
use GraphQL\Language\AST\Node;
@@ -43,9 +44,12 @@ public function getVisitor(QueryValidationContext $context): array
4344
);
4445
}
4546

47+
/**
48+
* @param OperationDefinitionNode|FieldNode|InlineFragmentNode|FragmentDefinitionNode $node
49+
*/
4650
protected function fieldDepth(Node $node, int $depth = 0, int $maxDepth = 0): int
4751
{
48-
if (isset($node->selectionSet) && $node->selectionSet instanceof SelectionSetNode) {
52+
if ($node->selectionSet instanceof SelectionSetNode) {
4953
foreach ($node->selectionSet->selections as $childNode) {
5054
$maxDepth = $this->nodeDepth($childNode, $depth, $maxDepth);
5155
}

src/Validator/Rules/UniqueDirectivesPerLocation.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public function getASTVisitor(ValidationContext $context): array
6060

6161
return [
6262
'enter' => static function (Node $node) use ($uniqueDirectiveMap, $context): void {
63-
if (! isset($node->directives)) {
63+
if (! property_exists($node, 'directives')) {
6464
return;
6565
}
6666

tests/Language/VisitorTest.php

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -176,32 +176,26 @@ public function testAllowsEditingNodeOnEnterAndOnLeave(): void
176176
{
177177
$ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]);
178178

179-
/** @var SelectionSetNode|null $selectionSet */
180-
$selectionSet = null;
179+
$directive1 = Parser::directive('@x');
180+
$directive2 = Parser::directive('@y');
181181

182182
$editedAst = Visitor::visit(
183183
$ast,
184184
[
185185
NodeKind::OPERATION_DEFINITION => [
186-
'enter' => function (OperationDefinitionNode $node) use (&$selectionSet, $ast): OperationDefinitionNode {
186+
'enter' => function (OperationDefinitionNode $node) use ($ast, $directive1): OperationDefinitionNode {
187187
$this->checkVisitorFnArgs($ast, \func_get_args());
188188

189-
$selectionSet = $node->selectionSet;
190-
191189
$newNode = clone $node;
192-
$newNode->selectionSet = new SelectionSetNode(['selections' => new NodeList([])]);
193-
$newNode->didEnter = true;
190+
$newNode->directives = new NodeList([$directive1]);
194191

195192
return $newNode;
196193
},
197-
'leave' => function (OperationDefinitionNode $node) use (&$selectionSet, $ast): OperationDefinitionNode {
194+
'leave' => function (OperationDefinitionNode $node) use ($ast, $directive2): OperationDefinitionNode {
198195
$this->checkVisitorFnArgs($ast, \func_get_args(), true);
199196

200-
self::assertInstanceOf(SelectionSetNode::class, $selectionSet);
201-
202197
$newNode = clone $node;
203-
$newNode->selectionSet = $selectionSet;
204-
$newNode->didLeave = true;
198+
$newNode->directives = $node->directives->merge([$directive2]);
205199

206200
return $newNode;
207201
},
@@ -211,47 +205,48 @@ public function testAllowsEditingNodeOnEnterAndOnLeave(): void
211205

212206
self::assertNotEquals($ast, $editedAst);
213207

214-
/** @var DocumentNode $expected */
215208
$expected = $ast->cloneDeep();
216-
$expected->definitions[0]->didEnter = true;
217-
$expected->definitions[0]->didLeave = true;
209+
$operationNode = $expected->definitions[0];
210+
assert($operationNode instanceof OperationDefinitionNode);
211+
$operationNode->directives = new NodeList([$directive1, $directive2]);
218212

219213
self::assertEquals($expected, $editedAst);
220214
}
221215

222216
public function testAllowsEditingRootNodeOnEnterAndLeave(): void
223217
{
224218
$ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]);
225-
$definitions = $ast->definitions;
219+
220+
$definition1 = Parser::operationDefinition('{ x }');
221+
$definition2 = Parser::operationDefinition('{ x }');
226222

227223
$editedAst = Visitor::visit(
228224
$ast,
229225
[
230226
NodeKind::DOCUMENT => [
231-
'enter' => function (DocumentNode $node) use ($ast): DocumentNode {
227+
'enter' => function (DocumentNode $node) use ($ast, $definition1): DocumentNode {
232228
$this->checkVisitorFnArgs($ast, \func_get_args());
233-
$tmp = clone $node;
234-
$tmp->definitions = new NodeList([]);
235-
$tmp->didEnter = true;
236229

237-
return $tmp;
230+
$newNode = clone $node;
231+
$newNode->definitions = $node->definitions->merge([$definition1]);
232+
233+
return $newNode;
238234
},
239-
'leave' => function (DocumentNode $node) use ($definitions, $ast): void {
235+
'leave' => function (DocumentNode $node) use ($ast, $definition2): void {
240236
$this->checkVisitorFnArgs($ast, \func_get_args(), true);
241-
$node->definitions = $definitions;
242-
$node->didLeave = true;
237+
238+
$node->definitions = $node->definitions->merge([$definition2]);
243239
},
244240
],
245241
]
246242
);
247243

248244
self::assertNotEquals($ast, $editedAst);
249245

250-
$tmp = $ast->cloneDeep();
251-
$tmp->didEnter = true;
252-
$tmp->didLeave = true;
246+
$expected = $ast->cloneDeep();
247+
$expected->definitions = $ast->definitions->merge([$definition1, $definition2]);
253248

254-
self::assertEquals($tmp, $editedAst);
249+
self::assertEquals($expected, $editedAst);
255250
}
256251

257252
public function testAllowsForEditingOnEnter(): void
@@ -367,7 +362,7 @@ public function testAllowsSkippingASubTree(): void
367362
[
368363
'enter' => function (Node $node) use (&$visited, $ast): ?VisitorOperation {
369364
$this->checkVisitorFnArgs($ast, \func_get_args());
370-
$visited[] = ['enter', $node->kind, $node->value ?? null];
365+
$visited[] = ['enter', $node->kind, property_exists($node, 'value') ? $node->value : null];
371366
if ($node instanceof FieldNode && $node->name->value === 'b') {
372367
return Visitor::skipNode();
373368
}
@@ -376,7 +371,7 @@ public function testAllowsSkippingASubTree(): void
376371
},
377372
'leave' => function (Node $node) use (&$visited, $ast): void {
378373
$this->checkVisitorFnArgs($ast, \func_get_args());
379-
$visited[] = ['leave', $node->kind, $node->value ?? null];
374+
$visited[] = ['leave', $node->kind, property_exists($node, 'value') ? $node->value : null];
380375
},
381376
]
382377
);
@@ -412,7 +407,7 @@ public function testAllowsEarlyExitWhileVisiting(): void
412407
[
413408
'enter' => function (Node $node) use (&$visited, $ast): ?VisitorOperation {
414409
$this->checkVisitorFnArgs($ast, \func_get_args());
415-
$visited[] = ['enter', $node->kind, $node->value ?? null];
410+
$visited[] = ['enter', $node->kind, property_exists($node, 'value') ? $node->value : null];
416411
if ($node instanceof NameNode && $node->value === 'x') {
417412
return Visitor::stop();
418413
}
@@ -421,7 +416,7 @@ public function testAllowsEarlyExitWhileVisiting(): void
421416
},
422417
'leave' => function (Node $node) use (&$visited, $ast): void {
423418
$this->checkVisitorFnArgs($ast, \func_get_args());
424-
$visited[] = ['leave', $node->kind, $node->value ?? null];
419+
$visited[] = ['leave', $node->kind, property_exists($node, 'value') ? $node->value : null];
425420
},
426421
]
427422
);

0 commit comments

Comments
 (0)