SchemaExtender::isSpecifiedScalarType()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 9
rs 9.2222
ccs 7
cts 7
cp 1
cc 6
nc 6
nop 1
crap 6
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
        ]);
395
    }
396
397 47
    protected static function extendInterfaceType(InterfaceType $type) : InterfaceType
398
    {
399 47
        return new InterfaceType([
400 47
            'name' => $type->name,
401 47
            'description' => $type->description,
402
            'fields' => static function () use ($type) {
403 46
                return static::extendFieldMap($type);
404 47
            },
405 47
            'astNode' => $type->astNode,
406 47
            'extensionASTNodes' => static::getExtensionASTNodes($type),
407 47
            'resolveType' => $type->config['resolveType'] ?? null,
408
        ]);
409
    }
410
411 60
    protected static function isSpecifiedScalarType(Type $type) : bool
412
    {
413 60
        return $type instanceof NamedType &&
414
            (
415 60
                $type->name === Type::STRING ||
416 60
                $type->name === Type::INT ||
417 60
                $type->name === Type::FLOAT ||
418 60
                $type->name === Type::BOOLEAN ||
419 60
                $type->name === Type::ID
420
            );
421
    }
422
423 60
    protected static function extendNamedType(Type $type)
424
    {
425 60
        if (Introspection::isIntrospectionType($type) || static::isSpecifiedScalarType($type)) {
426 57
            return $type;
427
        }
428
429 60
        $name = $type->name;
430 60
        if (! isset(static::$extendTypeCache[$name])) {
431 60
            if ($type instanceof ScalarType) {
432 43
                static::$extendTypeCache[$name] = static::extendScalarType($type);
433 60
            } elseif ($type instanceof ObjectType) {
434 59
                static::$extendTypeCache[$name] = static::extendObjectType($type);
435 53
            } elseif ($type instanceof InterfaceType) {
436 47
                static::$extendTypeCache[$name] = static::extendInterfaceType($type);
437 50
            } elseif ($type instanceof UnionType) {
438 46
                static::$extendTypeCache[$name] = static::extendUnionType($type);
439 47
            } elseif ($type instanceof EnumType) {
440 44
                static::$extendTypeCache[$name] = static::extendEnumType($type);
441 45
            } elseif ($type instanceof InputObjectType) {
442 45
                static::$extendTypeCache[$name] = static::extendInputObjectType($type);
443
            }
444
        }
445
446 60
        return static::$extendTypeCache[$name];
447
    }
448
449
    /**
450
     * @return mixed|null
451
     */
452 60
    protected static function extendMaybeNamedType(?NamedType $type = null)
453
    {
454 60
        if ($type !== null) {
455 59
            return static::extendNamedType($type);
456
        }
457
458 59
        return null;
459
    }
460
461
    /**
462
     * @param DirectiveDefinitionNode[] $directiveDefinitions
463
     *
464
     * @return Directive[]
465
     */
466 56
    protected static function getMergedDirectives(Schema $schema, array $directiveDefinitions) : array
467
    {
468
        $existingDirectives = array_map(static function (Directive $directive) {
469 56
            return static::extendDirective($directive);
470 56
        }, $schema->getDirectives());
471
472 56
        Utils::invariant(count($existingDirectives) > 0, 'schema must have default directives');
473
474 56
        return array_merge(
475 56
            $existingDirectives,
476
            array_map(static function (DirectiveDefinitionNode $directive) {
477 9
                return static::$astBuilder->buildDirective($directive);
478 56
            }, $directiveDefinitions)
479
        );
480
    }
481
482 56
    protected static function extendDirective(Directive $directive) : Directive
483
    {
484 56
        return new Directive([
485 56
            'name' => $directive->name,
486 56
            'description' => $directive->description,
487 56
            'locations' => $directive->locations,
488 56
            'args' => static::extendArgs($directive->args),
489 56
            'astNode' => $directive->astNode,
490
        ]);
491
    }
492
493
    /**
494
     * @param mixed[]|null $options
495
     */
496 66
    public static function extend(Schema $schema, DocumentNode $documentAST, ?array $options = null) : Schema
497
    {
498 66
        if ($options === null || ! (isset($options['assumeValid']) || isset($options['assumeValidSDL']))) {
499 64
            DocumentValidator::assertValidSDLExtension($documentAST, $schema);
500
        }
501
502 65
        $typeDefinitionMap         = [];
503 65
        static::$typeExtensionsMap = [];
504 65
        $directiveDefinitions      = [];
505
        /** @var SchemaDefinitionNode|null $schemaDef */
506 65
        $schemaDef = null;
507
        /** @var SchemaTypeExtensionNode[] $schemaExtensions */
508 65
        $schemaExtensions = [];
509
510 65
        $definitionsCount = count($documentAST->definitions);
511 65
        for ($i = 0; $i < $definitionsCount; $i++) {
512
513
            /** @var Node $def */
514 65
            $def = $documentAST->definitions[$i];
515
516 65
            if ($def instanceof SchemaDefinitionNode) {
517 1
                $schemaDef = $def;
518 64
            } elseif ($def instanceof SchemaTypeExtensionNode) {
519 9
                $schemaExtensions[] = $def;
520 62
            } elseif ($def instanceof TypeDefinitionNode) {
521 21
                $typeName = isset($def->name) ? $def->name->value : null;
522
523
                try {
524 21
                    $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

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