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.

SchemaExtender::extendObjectType()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
dl 0
loc 15
ccs 11
cts 11
cp 1
rs 9.9
c 1
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Utils;
6
7
use GraphQL\Error\Error;
8
use GraphQL\Language\AST\DirectiveDefinitionNode;
9
use GraphQL\Language\AST\DocumentNode;
10
use GraphQL\Language\AST\EnumTypeExtensionNode;
11
use GraphQL\Language\AST\InputObjectTypeExtensionNode;
12
use GraphQL\Language\AST\InterfaceTypeExtensionNode;
13
use GraphQL\Language\AST\Node;
14
use GraphQL\Language\AST\ObjectTypeExtensionNode;
15
use GraphQL\Language\AST\SchemaDefinitionNode;
16
use GraphQL\Language\AST\SchemaTypeExtensionNode;
17
use GraphQL\Language\AST\TypeDefinitionNode;
18
use GraphQL\Language\AST\TypeExtensionNode;
19
use GraphQL\Language\AST\UnionTypeExtensionNode;
20
use GraphQL\Type\Definition\CustomScalarType;
21
use GraphQL\Type\Definition\Directive;
22
use GraphQL\Type\Definition\EnumType;
23
use GraphQL\Type\Definition\EnumValueDefinition;
24
use GraphQL\Type\Definition\FieldArgument;
25
use GraphQL\Type\Definition\InputObjectType;
26
use GraphQL\Type\Definition\InterfaceType;
27
use GraphQL\Type\Definition\ListOfType;
28
use GraphQL\Type\Definition\NamedType;
29
use GraphQL\Type\Definition\NonNull;
30
use GraphQL\Type\Definition\ObjectType;
31
use GraphQL\Type\Definition\ScalarType;
32
use GraphQL\Type\Definition\Type;
33
use GraphQL\Type\Definition\UnionType;
34
use GraphQL\Type\Introspection;
35
use GraphQL\Type\Schema;
36
use GraphQL\Validator\DocumentValidator;
37
use function array_keys;
38
use function array_map;
39
use function array_merge;
40
use function array_values;
41
use function count;
42
43
class SchemaExtender
44
{
45
    const SCHEMA_EXTENSION = 'SchemaExtension';
46
47
    /** @var Type[] */
48
    protected static $extendTypeCache;
49
50
    /** @var mixed[] */
51
    protected static $typeExtensionsMap;
52
53
    /** @var ASTDefinitionBuilder */
54
    protected static $astBuilder;
55
56
    /**
57
     * @return TypeExtensionNode[]|null
58
     */
59 60
    protected static function getExtensionASTNodes(NamedType $type) : ?array
60
    {
61 60
        if (! $type instanceof Type) {
62
            return null;
63
        }
64
65 60
        $name = $type->name;
66 60
        if ($type->extensionASTNodes !== null) {
67 59
            if (isset(static::$typeExtensionsMap[$name])) {
68 24
                return array_merge($type->extensionASTNodes, static::$typeExtensionsMap[$name]);
69
            }
70
71 55
            return $type->extensionASTNodes;
72
        }
73
74 53
        return static::$typeExtensionsMap[$name] ?? null;
75
    }
76
77
    /**
78
     * @throws Error
79
     */
80 39
    protected static function checkExtensionNode(Type $type, Node $node) : void
81
    {
82
        switch (true) {
83 39
            case $node instanceof ObjectTypeExtensionNode:
84 25
                if (! ($type instanceof ObjectType)) {
85 1
                    throw new Error(
86 1
                        'Cannot extend non-object type "' . $type->name . '".',
87 1
                        [$node]
88
                    );
89
                }
90 24
                break;
91 23
            case $node instanceof InterfaceTypeExtensionNode:
92 10
                if (! ($type instanceof InterfaceType)) {
93 1
                    throw new Error(
94 1
                        'Cannot extend non-interface type "' . $type->name . '".',
95 1
                        [$node]
96
                    );
97
                }
98 9
                break;
99 17
            case $node instanceof EnumTypeExtensionNode:
100 7
                if (! ($type instanceof EnumType)) {
101 1
                    throw new Error(
102 1
                        'Cannot extend non-enum type "' . $type->name . '".',
103 1
                        [$node]
104
                    );
105
                }
106 6
                break;
107 13
            case $node instanceof UnionTypeExtensionNode:
108 9
                if (! ($type instanceof UnionType)) {
109 1
                    throw new Error(
110 1
                        'Cannot extend non-union type "' . $type->name . '".',
111 1
                        [$node]
112
                    );
113
                }
114 8
                break;
115 8
            case $node instanceof InputObjectTypeExtensionNode:
116 7
                if (! ($type instanceof InputObjectType)) {
117 1
                    throw new Error(
118 1
                        'Cannot extend non-input object type "' . $type->name . '".',
119 1
                        [$node]
120
                    );
121
                }
122 6
                break;
123
        }
124 38
    }
125
126 43
    protected static function extendScalarType(ScalarType $type) : CustomScalarType
127
    {
128 43
        return new CustomScalarType([
129 43
            'name' => $type->name,
130 43
            'description' => $type->description,
131 43
            'astNode' => $type->astNode,
132 43
            'serialize' => $type->config['serialize'] ?? null,
133 43
            'parseValue' => $type->config['parseValue'] ?? null,
134 43
            'parseLiteral' => $type->config['parseLiteral'] ?? null,
135 43
            'extensionASTNodes' => static::getExtensionASTNodes($type),
136
        ]);
137
    }
138
139 46
    protected static function extendUnionType(UnionType $type) : UnionType
140
    {
141 46
        return new UnionType([
142 46
            'name' => $type->name,
143 46
            'description' => $type->description,
144
            'types' => static function () use ($type) {
145 45
                return static::extendPossibleTypes($type);
146 46
            },
147 46
            'astNode' => $type->astNode,
148 46
            'resolveType' => $type->config['resolveType'] ?? null,
149 46
            'extensionASTNodes' => static::getExtensionASTNodes($type),
150
        ]);
151
    }
152
153 44
    protected static function extendEnumType(EnumType $type) : EnumType
154
    {
155 44
        return new EnumType([
156 44
            'name' => $type->name,
157 44
            'description' => $type->description,
158 44
            'values' => static::extendValueMap($type),
159 43
            'astNode' => $type->astNode,
160 43
            'extensionASTNodes' => static::getExtensionASTNodes($type),
161
        ]);
162
    }
163
164 45
    protected static function extendInputObjectType(InputObjectType $type) : InputObjectType
165
    {
166 45
        return new InputObjectType([
167 45
            'name' => $type->name,
168 45
            'description' => $type->description,
169
            'fields' => static function () use ($type) {
170 45
                return static::extendInputFieldMap($type);
171 45
            },
172 45
            'astNode' => $type->astNode,
173 45
            'extensionASTNodes' => static::getExtensionASTNodes($type),
174
        ]);
175
    }
176
177
    /**
178
     * @return mixed[]
179
     */
180 45
    protected static function extendInputFieldMap(InputObjectType $type) : array
181
    {
182 45
        $newFieldMap = [];
183 45
        $oldFieldMap = $type->getFields();
184 45
        foreach ($oldFieldMap as $fieldName => $field) {
185 44
            $newFieldMap[$fieldName] = [
186 44
                'description' => $field->description,
187 44
                'type' => static::extendType($field->type),
188 44
                'astNode' => $field->astNode,
189
            ];
190
191 44
            if (! $field->defaultValueExists()) {
192 44
                continue;
193
            }
194
195
            $newFieldMap[$fieldName]['defaultValue'] = $field->defaultValue;
196
        }
197
198 45
        $extensions = static::$typeExtensionsMap[$type->name] ?? null;
199 45
        if ($extensions !== null) {
200 6
            foreach ($extensions as $extension) {
201 6
                foreach ($extension->fields as $field) {
202 5
                    $fieldName = $field->name->value;
203 5
                    if (isset($oldFieldMap[$fieldName])) {
204 1
                        throw new Error('Field "' . $type->name . '.' . $fieldName . '" already exists in the schema. It cannot also be defined in this type extension.', [$field]);
205
                    }
206
207 5
                    $newFieldMap[$fieldName] = static::$astBuilder->buildInputField($field);
208
                }
209
            }
210
        }
211
212 45
        return $newFieldMap;
213
    }
214
215
    /**
216
     * @return mixed[]
217
     */
218 44
    protected static function extendValueMap(EnumType $type) : array
219
    {
220 44
        $newValueMap = [];
221
        /** @var EnumValueDefinition[] $oldValueMap */
222 44
        $oldValueMap = [];
223 44
        foreach ($type->getValues() as $value) {
224 43
            $oldValueMap[$value->name] = $value;
225
        }
226
227 44
        foreach ($oldValueMap as $key => $value) {
228 43
            $newValueMap[$key] = [
229 43
                'name' => $value->name,
230 43
                'description' => $value->description,
231 43
                'value' => $value->value,
232 43
                'deprecationReason' => $value->deprecationReason,
233 43
                'astNode' => $value->astNode,
234
            ];
235
        }
236
237 44
        $extensions = static::$typeExtensionsMap[$type->name] ?? null;
238 44
        if ($extensions !== null) {
239 6
            foreach ($extensions as $extension) {
240 6
                foreach ($extension->values as $value) {
241 5
                    $valueName = $value->name->value;
242 5
                    if (isset($oldValueMap[$valueName])) {
243 1
                        throw new Error('Enum value "' . $type->name . '.' . $valueName . '" already exists in the schema. It cannot also be defined in this type extension.', [$value]);
244
                    }
245 5
                    $newValueMap[$valueName] = static::$astBuilder->buildEnumValue($value);
246
                }
247
            }
248
        }
249
250 43
        return $newValueMap;
251
    }
252
253
    /**
254
     * @return ObjectType[]
255
     */
256 45
    protected static function extendPossibleTypes(UnionType $type) : array
257
    {
258
        $possibleTypes = array_map(static function ($type) {
259 44
            return static::extendNamedType($type);
260 45
        }, $type->getTypes());
261
262 45
        $extensions = static::$typeExtensionsMap[$type->name] ?? null;
263 45
        if ($extensions !== null) {
264 8
            foreach ($extensions as $extension) {
265 8
                foreach ($extension->types as $namedType) {
266 8
                    $possibleTypes[] = static::$astBuilder->buildType($namedType);
267
                }
268
            }
269
        }
270
271 45
        return $possibleTypes;
272
    }
273
274
    /**
275
     * @return InterfaceType[]
276
     */
277 55
    protected static function extendImplementedInterfaces(ObjectType $type) : array
278
    {
279
        $interfaces = array_map(static function (InterfaceType $interfaceType) {
280 46
            return static::extendNamedType($interfaceType);
281 55
        }, $type->getInterfaces());
282
283 55
        $extensions = static::$typeExtensionsMap[$type->name] ?? null;
284 55
        if ($extensions !== null) {
285
            /** @var ObjectTypeExtensionNode $extension */
286 24
            foreach ($extensions as $extension) {
287 24
                foreach ($extension->interfaces as $namedType) {
288 24
                    $interfaces[] = static::$astBuilder->buildType($namedType);
289
                }
290
            }
291
        }
292
293 55
        return $interfaces;
294
    }
295
296 56
    protected static function extendType($typeDef)
297
    {
298 56
        if ($typeDef instanceof ListOfType) {
299 42
            return Type::listOf(static::extendType($typeDef->ofType));
300
        }
301
302 56
        if ($typeDef instanceof NonNull) {
303 56
            return Type::nonNull(static::extendType($typeDef->getWrappedType()));
0 ignored issues
show
Bug introduced by
It seems like static::extendType($typeDef->getWrappedType()) can also be of type GraphQL\Type\Definition\NonNull and GraphQL\Type\Definition\Type; however, parameter $wrappedType of GraphQL\Type\Definition\Type::nonNull() does only seem to accept GraphQL\Type\Definition\NullableType, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

303
            return Type::nonNull(/** @scrutinizer ignore-type */ static::extendType($typeDef->getWrappedType()));
Loading history...
304
        }
305
306 56
        return static::extendNamedType($typeDef);
307
    }
308
309
    /**
310
     * @param FieldArgument[] $args
311
     *
312
     * @return mixed[]
313
     */
314 56
    protected static function extendArgs(array $args) : array
315
    {
316 56
        return Utils::keyValMap(
317 56
            $args,
318
            static function (FieldArgument $arg) {
319 56
                return $arg->name;
320 56
            },
321
            static function (FieldArgument $arg) {
322
                $def = [
323 56
                    'type'        => static::extendType($arg->getType()),
324 56
                    'description' => $arg->description,
325 56
                    'astNode'     => $arg->astNode,
326
                ];
327
328 56
                if ($arg->defaultValueExists()) {
329 55
                    $def['defaultValue'] = $arg->defaultValue;
330
                }
331
332 56
                return $def;
333 56
            }
334
        );
335
    }
336
337
    /**
338
     * @param InterfaceType|ObjectType $type
339
     *
340
     * @return mixed[]
341
     *
342
     * @throws Error
343
     */
344 55
    protected static function extendFieldMap($type) : array
345
    {
346 55
        $newFieldMap = [];
347 55
        $oldFieldMap = $type->getFields();
348
349 55
        foreach (array_keys($oldFieldMap) as $fieldName) {
350 55
            $field = $oldFieldMap[$fieldName];
351
352 55
            $newFieldMap[$fieldName] = [
353 55
                'name' => $fieldName,
354 55
                'description' => $field->description,
355 55
                'deprecationReason' => $field->deprecationReason,
356 55
                'type' => static::extendType($field->getType()),
357 55
                'args' => static::extendArgs($field->args),
358 55
                'astNode' => $field->astNode,
359 55
                'resolve' => $field->resolveFn,
360
            ];
361
        }
362
363 55
        $extensions = static::$typeExtensionsMap[$type->name] ?? null;
364 55
        if ($extensions !== null) {
365 26
            foreach ($extensions as $extension) {
366 26
                foreach ($extension->fields as $field) {
367 24
                    $fieldName = $field->name->value;
368 24
                    if (isset($oldFieldMap[$fieldName])) {
369 1
                        throw new Error('Field "' . $type->name . '.' . $fieldName . '" already exists in the schema. It cannot also be defined in this type extension.', [$field]);
370
                    }
371
372 25
                    $newFieldMap[$fieldName] = static::$astBuilder->buildField($field);
373
                }
374
            }
375
        }
376
377 55
        return $newFieldMap;
378
    }
379
380 59
    protected static function extendObjectType(ObjectType $type) : ObjectType
381
    {
382 59
        return new ObjectType([
383 59
            'name' => $type->name,
384 59
            'description' => $type->description,
385
            'interfaces' => static function () use ($type) {
386 55
                return static::extendImplementedInterfaces($type);
387 59
            },
388
            'fields' => static function () use ($type) {
389 55
                return static::extendFieldMap($type);
390 59
            },
391 59
            'astNode' => $type->astNode,
392 59
            'extensionASTNodes' => static::getExtensionASTNodes($type),
393 59
            'isTypeOf' => $type->config['isTypeOf'] ?? null,
394
            'resolveField' => $type->resolveFieldFn ?? null,
395
        ]);
396
    }
397 47
398
    protected static function extendInterfaceType(InterfaceType $type) : InterfaceType
399 47
    {
400 47
        return new InterfaceType([
401 47
            'name' => $type->name,
402
            'description' => $type->description,
403 46
            'fields' => static function () use ($type) {
404 47
                return static::extendFieldMap($type);
405 47
            },
406 47
            'astNode' => $type->astNode,
407 47
            'extensionASTNodes' => static::getExtensionASTNodes($type),
408
            'resolveType' => $type->config['resolveType'] ?? null,
409
        ]);
410
    }
411 60
412
    protected static function isSpecifiedScalarType(Type $type) : bool
413 60
    {
414
        return $type instanceof NamedType &&
415 60
            (
416 60
                $type->name === Type::STRING ||
417 60
                $type->name === Type::INT ||
418 60
                $type->name === Type::FLOAT ||
419 60
                $type->name === Type::BOOLEAN ||
420
                $type->name === Type::ID
421
            );
422
    }
423 60
424
    protected static function extendNamedType(Type $type)
425 60
    {
426 57
        if (Introspection::isIntrospectionType($type) || static::isSpecifiedScalarType($type)) {
427
            return $type;
428
        }
429 60
430 60
        $name = $type->name;
431 60
        if (! isset(static::$extendTypeCache[$name])) {
432 43
            if ($type instanceof ScalarType) {
433 60
                static::$extendTypeCache[$name] = static::extendScalarType($type);
434 59
            } elseif ($type instanceof ObjectType) {
435 53
                static::$extendTypeCache[$name] = static::extendObjectType($type);
436 47
            } elseif ($type instanceof InterfaceType) {
437 50
                static::$extendTypeCache[$name] = static::extendInterfaceType($type);
438 46
            } elseif ($type instanceof UnionType) {
439 47
                static::$extendTypeCache[$name] = static::extendUnionType($type);
440 44
            } elseif ($type instanceof EnumType) {
441 45
                static::$extendTypeCache[$name] = static::extendEnumType($type);
442 45
            } elseif ($type instanceof InputObjectType) {
443
                static::$extendTypeCache[$name] = static::extendInputObjectType($type);
444
            }
445
        }
446 60
447
        return static::$extendTypeCache[$name];
448
    }
449
450
    /**
451
     * @return mixed|null
452 60
     */
453
    protected static function extendMaybeNamedType(?NamedType $type = null)
454 60
    {
455 59
        if ($type !== null) {
456
            return static::extendNamedType($type);
457
        }
458 59
459
        return null;
460
    }
461
462
    /**
463
     * @param DirectiveDefinitionNode[] $directiveDefinitions
464
     *
465
     * @return Directive[]
466 56
     */
467
    protected static function getMergedDirectives(Schema $schema, array $directiveDefinitions) : array
468
    {
469 56
        $existingDirectives = array_map(static function (Directive $directive) {
470 56
            return static::extendDirective($directive);
471
        }, $schema->getDirectives());
472 56
473
        Utils::invariant(count($existingDirectives) > 0, 'schema must have default directives');
474 56
475 56
        return array_merge(
476
            $existingDirectives,
477 9
            array_map(static function (DirectiveDefinitionNode $directive) {
478 56
                return static::$astBuilder->buildDirective($directive);
479
            }, $directiveDefinitions)
480
        );
481
    }
482 56
483
    protected static function extendDirective(Directive $directive) : Directive
484 56
    {
485 56
        return new Directive([
486 56
            'name' => $directive->name,
487 56
            'description' => $directive->description,
488 56
            'locations' => $directive->locations,
489 56
            'args' => static::extendArgs($directive->args),
490
            'astNode' => $directive->astNode,
491
        ]);
492
    }
493
494
    /**
495
     * @param mixed[]|null $options
496 66
     */
497
    public static function extend(Schema $schema, DocumentNode $documentAST, ?array $options = null) : Schema
498 66
    {
499 64
        if ($options === null || ! (isset($options['assumeValid']) || isset($options['assumeValidSDL']))) {
500
            DocumentValidator::assertValidSDLExtension($documentAST, $schema);
501
        }
502 65
503 65
        $typeDefinitionMap         = [];
504 65
        static::$typeExtensionsMap = [];
505
        $directiveDefinitions      = [];
506 65
        /** @var SchemaDefinitionNode|null $schemaDef */
507
        $schemaDef = null;
508 65
        /** @var SchemaTypeExtensionNode[] $schemaExtensions */
509
        $schemaExtensions = [];
510 65
511 65
        $definitionsCount = count($documentAST->definitions);
512
        for ($i = 0; $i < $definitionsCount; $i++) {
513
514 65
            /** @var Node $def */
515
            $def = $documentAST->definitions[$i];
516 65
517 1
            if ($def instanceof SchemaDefinitionNode) {
518 64
                $schemaDef = $def;
519 9
            } elseif ($def instanceof SchemaTypeExtensionNode) {
520 62
                $schemaExtensions[] = $def;
521 21
            } elseif ($def instanceof TypeDefinitionNode) {
522
                $typeName = isset($def->name) ? $def->name->value : null;
523
524 21
                try {
525 2
                    $type = $schema->getType($typeName);
0 ignored issues
show
Bug introduced by
It seems like $typeName can also be of type null; however, parameter $name of GraphQL\Type\Schema::getType() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

525
                    $type = $schema->getType(/** @scrutinizer ignore-type */ $typeName);
Loading history...
526 2
                } catch (Error $error) {
527
                    $type = null;
528
                }
529 21
530 1
                if ($type) {
531
                    throw new Error('Type "' . $typeName . '" already exists in the schema. It cannot also be defined in this type definition.', [$def]);
532 20
                }
533 47
                $typeDefinitionMap[$typeName] = $def;
534 40
            } elseif ($def instanceof TypeExtensionNode) {
535 40
                $extendedTypeName = isset($def->name) ? $def->name->value : null;
536 40
                $existingType     = $schema->getType($extendedTypeName);
537 1
                if ($existingType === null) {
538
                    throw new Error('Cannot extend type "' . $extendedTypeName . '" because it does not exist in the existing schema.', [$def]);
539
                }
540 39
541
                static::checkExtensionNode($existingType, $def);
542 38
543 38
                $existingTypeExtensions                       = static::$typeExtensionsMap[$extendedTypeName] ?? null;
544 11
                static::$typeExtensionsMap[$extendedTypeName] = $existingTypeExtensions !== null ? array_merge($existingTypeExtensions, [$def]) : [$def];
545 10
            } elseif ($def instanceof DirectiveDefinitionNode) {
546 10
                $directiveName     = $def->name->value;
547 10
                $existingDirective = $schema->getDirective($directiveName);
548 2
                if ($existingDirective !== null) {
549
                    throw new Error('Directive "' . $directiveName . '" already exists in the schema. It cannot be redefined.', [$def]);
550 9
                }
551
                $directiveDefinitions[] = $def;
552
            }
553
        }
554 61
555 61
        if (count(static::$typeExtensionsMap) === 0 &&
556 61
            count($typeDefinitionMap) === 0 &&
557 61
            count($directiveDefinitions) === 0 &&
558 61
            count($schemaExtensions) === 0 &&
559
            $schemaDef === null
560 1
        ) {
561
            return $schema;
562
        }
563 60
564 60
        static::$astBuilder = new ASTDefinitionBuilder(
565 60
            $typeDefinitionMap,
566
            $options,
567
            static function (string $typeName) use ($schema) {
568 12
                /** @var ScalarType|ObjectType|InterfaceType|UnionType|EnumType|InputObjectType $existingType */
569 12
                $existingType = $schema->getType($typeName);
570 11
                if ($existingType !== null) {
571
                    return static::extendNamedType($existingType);
572
                }
573 1
574 60
                throw new Error('Unknown type: "' . $typeName . '". Ensure that this type exists either in the original schema, or is added in a type definition.', [$typeName]);
575
            }
576
        );
577 60
578
        static::$extendTypeCache = [];
579
580 60
        $operationTypes = [
581 60
            'query' => static::extendMaybeNamedType($schema->getQueryType()),
582 60
            'mutation' => static::extendMaybeNamedType($schema->getMutationType()),
583
            'subscription' => static::extendMaybeNamedType($schema->getSubscriptionType()),
584
        ];
585 60
586 1
        if ($schemaDef) {
587 1
            foreach ($schemaDef->operationTypes as $operationType) {
588 1
                $operation = $operationType->operation;
589
                $type      = $operationType->type;
590 1
591
                if (isset($operationTypes[$operation])) {
592
                    throw new Error('Must provide only one ' . $operation . ' type in schema.');
593
                }
594 1
595
                $operationTypes[$operation] = static::$astBuilder->buildType($type);
596
            }
597
        }
598 60
599 9
        foreach ($schemaExtensions as $schemaExtension) {
600 2
            if (! $schemaExtension->operationTypes) {
601
                continue;
602
            }
603 8
604 8
            foreach ($schemaExtension->operationTypes as $operationType) {
605 8
                $operation = $operationType->operation;
606 3
                if (isset($operationTypes[$operation])) {
607
                    throw new Error('Must provide only one ' . $operation . ' type in schema.');
608 7
                }
609
                $operationTypes[$operation] = static::$astBuilder->buildType($operationType->type);
610
            }
611
        }
612 57
613 6
        $schemaExtensionASTNodes = array_merge($schema->extensionASTNodes, $schemaExtensions);
614 2
615 6
        $types = array_merge(
616 57
            // Iterate through all types, getting the type definition for each, ensuring
617
            // that any type not directly referenced by a field will get created.
618 57
            array_map(static function ($type) {
619
                return static::extendNamedType($type);
620
            }, array_values($schema->getTypeMap())),
621
            // Do the same with new types.
622 57
            array_map(static function ($type) {
623 57
                return static::$astBuilder->buildType($type);
624
            }, array_values($typeDefinitionMap))
625
        );
626 17
627 56
        return new Schema([
628
            'query' => $operationTypes['query'],
629
            'mutation' => $operationTypes['mutation'],
630 56
            'subscription' => $operationTypes['subscription'],
631 56
            'types' => $types,
632 56
            'directives' => static::getMergedDirectives($schema, $directiveDefinitions),
633 56
            'astNode' => $schema->getAstNode(),
634 56
            'extensionASTNodes' => $schemaExtensionASTNodes,
635 56
        ]);
636 56
    }
637
}
638