GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (162)

src/Utils/ASTDefinitionBuilder.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Utils;
6
7
use GraphQL\Error\Error;
8
use GraphQL\Executor\Values;
9
use GraphQL\Language\AST\DirectiveDefinitionNode;
10
use GraphQL\Language\AST\EnumTypeDefinitionNode;
11
use GraphQL\Language\AST\EnumValueDefinitionNode;
12
use GraphQL\Language\AST\FieldDefinitionNode;
13
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
14
use GraphQL\Language\AST\InputValueDefinitionNode;
15
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
16
use GraphQL\Language\AST\ListTypeNode;
17
use GraphQL\Language\AST\NamedTypeNode;
18
use GraphQL\Language\AST\Node;
19
use GraphQL\Language\AST\NonNullTypeNode;
20
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
21
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
22
use GraphQL\Language\AST\TypeNode;
23
use GraphQL\Language\AST\UnionTypeDefinitionNode;
24
use GraphQL\Language\Token;
25
use GraphQL\Type\Definition\CustomScalarType;
26
use GraphQL\Type\Definition\Directive;
27
use GraphQL\Type\Definition\EnumType;
28
use GraphQL\Type\Definition\FieldArgument;
29
use GraphQL\Type\Definition\InputObjectType;
30
use GraphQL\Type\Definition\InputType;
31
use GraphQL\Type\Definition\InterfaceType;
32
use GraphQL\Type\Definition\ObjectType;
33
use GraphQL\Type\Definition\Type;
34
use GraphQL\Type\Definition\UnionType;
35
use Throwable;
36
use function array_reverse;
37
use function implode;
38
use function is_array;
39
use function is_string;
40
use function sprintf;
41
42
class ASTDefinitionBuilder
43
{
44
    /** @var Node[] */
45
    private $typeDefinitionsMap;
46
47
    /** @var callable */
48
    private $typeConfigDecorator;
49
50
    /** @var bool[] */
51
    private $options;
52
53
    /** @var callable */
54
    private $resolveType;
55
56
    /** @var Type[] */
57
    private $cache;
58
59
    /**
60
     * @param Node[] $typeDefinitionsMap
61
     * @param bool[] $options
62
     */
63 194
    public function __construct(
64
        array $typeDefinitionsMap,
65
        $options,
66
        callable $resolveType,
67
        ?callable $typeConfigDecorator = null
68
    ) {
69 194
        $this->typeDefinitionsMap  = $typeDefinitionsMap;
70 194
        $this->typeConfigDecorator = $typeConfigDecorator;
71 194
        $this->options             = $options;
72 194
        $this->resolveType         = $resolveType;
73
74 194
        $this->cache = Type::getAllBuiltInTypes();
75 194
    }
76
77 37
    public function buildDirective(DirectiveDefinitionNode $directiveNode)
78
    {
79 37
        return new Directive([
80 37
            'name'        => $directiveNode->name->value,
81 37
            'description' => $this->getDescription($directiveNode),
82 37
            'locations'   => Utils::map(
83 37
                $directiveNode->locations,
84
                static function ($node) {
85 37
                    return $node->value;
86 37
                }
87
            ),
88 37
            'args'        => $directiveNode->arguments ? FieldArgument::createMap($this->makeInputValues($directiveNode->arguments)) : null,
89 37
            'astNode'     => $directiveNode,
90
        ]);
91
    }
92
93
    /**
94
     * Given an ast node, returns its string description.
95
     */
96 184
    private function getDescription($node)
97
    {
98 184
        if ($node->description) {
99 7
            return $node->description->value;
100
        }
101 181
        if (isset($this->options['commentDescriptions'])) {
102 3
            $rawValue = $this->getLeadingCommentBlock($node);
103 3
            if ($rawValue !== null) {
104 3
                return BlockString::value("\n" . $rawValue);
105
            }
106
        }
107
108 178
        return null;
109
    }
110
111 3
    private function getLeadingCommentBlock($node)
112
    {
113 3
        $loc = $node->loc;
114 3
        if (! $loc || ! $loc->startToken) {
115
            return null;
116
        }
117 3
        $comments = [];
118 3
        $token    = $loc->startToken->prev;
119 3
        while ($token &&
120 3
            $token->kind === Token::COMMENT &&
121 3
            $token->next && $token->prev &&
122 3
            $token->line + 1 === $token->next->line &&
123 3
            $token->line !== $token->prev->line
124
        ) {
125 3
            $value      = $token->value;
126 3
            $comments[] = $value;
127 3
            $token      = $token->prev;
128
        }
129
130 3
        return implode("\n", array_reverse($comments));
131
    }
132
133 169
    private function makeInputValues($values)
134
    {
135 169
        return Utils::keyValMap(
136 169
            $values,
137
            static function ($value) {
138 61
                return $value->name->value;
139 169
            },
140
            function ($value) {
141
                // Note: While this could make assertions to get the correctly typed
142
                // value, that would throw immediately while type system validation
143
                // with validateSchema() will produce more actionable results.
144 61
                $type = $this->buildWrappedType($value->type);
145
146
                $config = [
147 61
                    'name'        => $value->name->value,
148 61
                    'type'        => $type,
149 61
                    'description' => $this->getDescription($value),
150 61
                    'astNode'     => $value,
151
                ];
152 61
                if (isset($value->defaultValue)) {
153 8
                    $config['defaultValue'] = AST::valueFromAST($value->defaultValue, $type);
154
                }
155
156 61
                return $config;
157 169
            }
158
        );
159
    }
160
161
    /**
162
     * @return Type|InputType
163
     *
164
     * @throws Error
165
     */
166 152
    private function buildWrappedType(TypeNode $typeNode)
167
    {
168 152
        if ($typeNode instanceof ListTypeNode) {
169 14
            return Type::listOf($this->buildWrappedType($typeNode->type));
170
        }
171 152
        if ($typeNode instanceof NonNullTypeNode) {
172 21
            return Type::nonNull($this->buildWrappedType($typeNode->type));
173
        }
174
175 152
        return $this->buildType($typeNode);
176
    }
177
178
    /**
179
     * @param string|NamedTypeNode $ref
180
     *
181
     * @return Type
182
     *
183
     * @throws Error
184
     */
185 170
    public function buildType($ref)
186
    {
187 170
        if (is_string($ref)) {
188 132
            return $this->internalBuildType($ref);
189
        }
190
191 159
        return $this->internalBuildType($ref->name->value, $ref);
192
    }
193
194
    /**
195
     * @param string             $typeName
196
     * @param NamedTypeNode|null $typeNode
197
     *
198
     * @return Type
199
     *
200
     * @throws Error
201
     */
202 170
    private function internalBuildType($typeName, $typeNode = null)
203
    {
204 170
        if (! isset($this->cache[$typeName])) {
205 155
            if (isset($this->typeDefinitionsMap[$typeName])) {
206 149
                $type = $this->makeSchemaDef($this->typeDefinitionsMap[$typeName]);
207 149
                if ($this->typeConfigDecorator) {
208 2
                    $fn = $this->typeConfigDecorator;
209
                    try {
210 2
                        $config = $fn($type->config, $this->typeDefinitionsMap[$typeName], $this->typeDefinitionsMap);
211
                    } catch (Throwable $e) {
212
                        throw new Error(
213
                            sprintf('Type config decorator passed to %s threw an error ', static::class) .
214
                            sprintf('when building %s type: %s', $typeName, $e->getMessage()),
215
                            null,
216
                            null,
217
                            null,
218
                            null,
219
                            $e
220
                        );
221
                    }
222 2
                    if (! is_array($config) || isset($config[0])) {
223
                        throw new Error(
224
                            sprintf(
225
                                'Type config decorator passed to %s is expected to return an array, but got %s',
226
                                static::class,
227
                                Utils::getVariableType($config)
228
                            )
229
                        );
230
                    }
231 2
                    $type = $this->makeSchemaDefFromConfig($this->typeDefinitionsMap[$typeName], $config);
232
                }
233 149
                $this->cache[$typeName] = $type;
234
            } else {
235 16
                $fn                     = $this->resolveType;
236 16
                $this->cache[$typeName] = $fn($typeName, $typeNode);
237
            }
238
        }
239
240 169
        return $this->cache[$typeName];
241
    }
242
243
    /**
244
     * @param ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode|EnumTypeDefinitionNode|ScalarTypeDefinitionNode|InputObjectTypeDefinitionNode|UnionTypeDefinitionNode $def
245
     *
246
     * @return CustomScalarType|EnumType|InputObjectType|InterfaceType|ObjectType|UnionType
247
     *
248
     * @throws Error
249
     */
250 149
    private function makeSchemaDef(Node $def)
251
    {
252
        switch (true) {
253 149
            case $def instanceof ObjectTypeDefinitionNode:
254 143
                return $this->makeTypeDef($def);
255 83
            case $def instanceof InterfaceTypeDefinitionNode:
256 38
                return $this->makeInterfaceDef($def);
257 55
            case $def instanceof EnumTypeDefinitionNode:
258 19
                return $this->makeEnumDef($def);
259 42
            case $def instanceof UnionTypeDefinitionNode:
260 20
                return $this->makeUnionDef($def);
261 29
            case $def instanceof ScalarTypeDefinitionNode:
262 8
                return $this->makeScalarDef($def);
263 25
            case $def instanceof InputObjectTypeDefinitionNode:
264 25
                return $this->makeInputObjectDef($def);
265
            default:
266
                throw new Error(sprintf('Type kind of %s not supported.', $def->kind));
267
        }
268
    }
269
270 143
    private function makeTypeDef(ObjectTypeDefinitionNode $def)
271
    {
272 143
        $typeName = $def->name->value;
273
274 143
        return new ObjectType([
275 143
            'name'        => $typeName,
276 143
            'description' => $this->getDescription($def),
277
            'fields'      => function () use ($def) {
278 128
                return $this->makeFieldDefMap($def);
279 143
            },
280
            'interfaces'  => function () use ($def) {
281 125
                return $this->makeImplementedInterfaces($def);
282 143
            },
283 143
            'astNode'     => $def,
284
        ]);
285
    }
286
287 129
    private function makeFieldDefMap($def)
288
    {
289 129
        return $def->fields
290 129
            ? Utils::keyValMap(
291 129
                $def->fields,
292
                static function ($field) {
293 129
                    return $field->name->value;
294 129
                },
295
                function ($field) {
296 129
                    return $this->buildField($field);
297 129
                }
298
            )
299 128
            : [];
300
    }
301
302 145
    public function buildField(FieldDefinitionNode $field)
303
    {
304
        return [
305
            // Note: While this could make assertions to get the correctly typed
306
            // value, that would throw immediately while type system validation
307
            // with validateSchema() will produce more actionable results.
308 145
            'type'              => $this->buildWrappedType($field->type),
309 143
            'description'       => $this->getDescription($field),
310 143
            'args'              => $field->arguments ? $this->makeInputValues($field->arguments) : null,
311 143
            'deprecationReason' => $this->getDeprecationReason($field),
312 143
            'astNode'           => $field,
313
        ];
314
    }
315
316
    /**
317
     * Given a collection of directives, returns the string value for the
318
     * deprecation reason.
319
     *
320
     * @param EnumValueDefinitionNode | FieldDefinitionNode $node
321
     *
322
     * @return string
323
     */
324 146
    private function getDeprecationReason($node)
325
    {
326 146
        $deprecated = Values::getDirectiveValues(Directive::deprecatedDirective(), $node);
327
328 146
        return $deprecated['reason'] ?? null;
329
    }
330
331 125
    private function makeImplementedInterfaces(ObjectTypeDefinitionNode $def)
332
    {
333 125
        if ($def->interfaces) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $def->interfaces of type GraphQL\Language\AST\NamedTypeNode[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
334
            // Note: While this could make early assertions to get the correctly
335
            // typed values, that would throw immediately while type system
336
            // validation with validateSchema() will produce more actionable results.
337 36
            return Utils::map(
338 36
                $def->interfaces,
339
                function ($iface) {
340 36
                    return $this->buildType($iface);
341 36
                }
342
            );
343
        }
344
345 119
        return null;
346
    }
347
348 38
    private function makeInterfaceDef(InterfaceTypeDefinitionNode $def)
349
    {
350 38
        $typeName = $def->name->value;
351
352 38
        return new InterfaceType([
353 38
            'name'        => $typeName,
354 38
            'description' => $this->getDescription($def),
355
            'fields'      => function () use ($def) {
356 38
                return $this->makeFieldDefMap($def);
357 38
            },
358 38
            'astNode'     => $def,
359
        ]);
360
    }
361
362 19
    private function makeEnumDef(EnumTypeDefinitionNode $def)
363
    {
364 19
        return new EnumType([
365 19
            'name'        => $def->name->value,
366 19
            'description' => $this->getDescription($def),
367 19
            'values'      => $def->values
368 19
                ? Utils::keyValMap(
369 19
                    $def->values,
370
                    static function ($enumValue) {
371 18
                        return $enumValue->name->value;
372 19
                    },
373
                    function ($enumValue) {
374
                        return [
375 18
                            'description'       => $this->getDescription($enumValue),
376 18
                            'deprecationReason' => $this->getDeprecationReason($enumValue),
377 18
                            'astNode'           => $enumValue,
378
                        ];
379 19
                    }
380
                )
381
                : [],
382 19
            'astNode'     => $def,
383
        ]);
384
    }
385
386 20
    private function makeUnionDef(UnionTypeDefinitionNode $def)
387
    {
388 20
        return new UnionType([
389 20
            'name'        => $def->name->value,
390 20
            'description' => $this->getDescription($def),
391
            // Note: While this could make assertions to get the correctly typed
392
            // values below, that would throw immediately while type system
393
            // validation with validateSchema() will produce more actionable results.
394 20
            'types'       => $def->types
395
                ? function () use ($def) {
396 19
                    return Utils::map(
397 19
                        $def->types,
398
                        function ($typeNode) {
399 19
                            return $this->buildType($typeNode);
400 19
                        }
401
                    );
402 19
                }
403
                : [],
404 20
            'astNode'     => $def,
405
        ]);
406
    }
407
408 8
    private function makeScalarDef(ScalarTypeDefinitionNode $def)
409
    {
410 8
        return new CustomScalarType([
411 8
            'name'        => $def->name->value,
412 8
            'description' => $this->getDescription($def),
413 8
            'astNode'     => $def,
414
            'serialize'   => static function ($value) {
415 1
                return $value;
416 8
            },
417
        ]);
418
    }
419
420 25
    private function makeInputObjectDef(InputObjectTypeDefinitionNode $def)
421
    {
422 25
        return new InputObjectType([
423 25
            'name'        => $def->name->value,
424 25
            'description' => $this->getDescription($def),
425
            'fields'      => function () use ($def) {
426 25
                return $def->fields
427 25
                    ? $this->makeInputValues($def->fields)
428 25
                    : [];
429 25
            },
430 25
            'astNode'     => $def,
431
        ]);
432
    }
433
434
    /**
435
     * @param mixed[] $config
436
     *
437
     * @return CustomScalarType|EnumType|InputObjectType|InterfaceType|ObjectType|UnionType
438
     *
439
     * @throws Error
440
     */
441 2
    private function makeSchemaDefFromConfig(Node $def, array $config)
442
    {
443
        switch (true) {
444 2
            case $def instanceof ObjectTypeDefinitionNode:
445 2
                return new ObjectType($config);
446 2
            case $def instanceof InterfaceTypeDefinitionNode:
447 2
                return new InterfaceType($config);
448 2
            case $def instanceof EnumTypeDefinitionNode:
449 2
                return new EnumType($config);
450
            case $def instanceof UnionTypeDefinitionNode:
451
                return new UnionType($config);
452
            case $def instanceof ScalarTypeDefinitionNode:
453
                return new CustomScalarType($config);
454
            case $def instanceof InputObjectTypeDefinitionNode:
455
                return new InputObjectType($config);
456
            default:
457
                throw new Error(sprintf('Type kind of %s not supported.', $def->kind));
458
        }
459
    }
460
461
    /**
462
     * @return mixed[]
463
     */
464 4
    public function buildInputField(InputValueDefinitionNode $value) : array
465
    {
466 4
        $type = $this->buildWrappedType($value->type);
467
468
        $config = [
469 3
            'name' => $value->name->value,
470 3
            'type' => $type,
471 3
            'description' => $this->getDescription($value),
472 3
            'astNode' => $value,
473
        ];
474
475 3
        if ($value->defaultValue) {
476
            $config['defaultValue'] = $value->defaultValue;
477
        }
478
479 3
        return $config;
480
    }
481
482
    /**
483
     * @return mixed[]
484
     */
485 4
    public function buildEnumValue(EnumValueDefinitionNode $value) : array
486
    {
487
        return [
488 4
            'description' => $this->getDescription($value),
489 4
            'deprecationReason' => $this->getDeprecationReason($value),
490 4
            'astNode' => $value,
491
        ];
492
    }
493
}
494