@@ -56,6 +56,134 @@ class SchemaExtender
56
56
57
57
protected static ASTDefinitionBuilder $ astBuilder ;
58
58
59
+ /**
60
+ * @param array<string, bool> $options
61
+ * @phpstan-param TypeConfigDecorator|null $typeConfigDecorator
62
+ */
63
+ public static function extend (
64
+ Schema $ schema ,
65
+ DocumentNode $ documentAST ,
66
+ array $ options = [],
67
+ ?callable $ typeConfigDecorator = null
68
+ ): Schema {
69
+ if (
70
+ ! ($ options ['assumeValid ' ] ?? false )
71
+ && ! ($ options ['assumeValidSDL ' ] ?? false )
72
+ ) {
73
+ DocumentValidator::assertValidSDLExtension ($ documentAST , $ schema );
74
+ }
75
+
76
+ /** @var array<string, Node&TypeDefinitionNode> $typeDefinitionMap */
77
+ $ typeDefinitionMap = [];
78
+
79
+ static ::$ typeExtensionsMap = [];
80
+
81
+ /** @var array<int, DirectiveDefinitionNode> $directiveDefinitions */
82
+ $ directiveDefinitions = [];
83
+
84
+ /** @var SchemaDefinitionNode|null $schemaDef */
85
+ $ schemaDef = null ;
86
+
87
+ /** @var array<int, SchemaExtensionNode> $schemaExtensions */
88
+ $ schemaExtensions = [];
89
+
90
+ foreach ($ documentAST ->definitions as $ def ) {
91
+ if ($ def instanceof SchemaDefinitionNode) {
92
+ $ schemaDef = $ def ;
93
+ } elseif ($ def instanceof SchemaExtensionNode) {
94
+ $ schemaExtensions [] = $ def ;
95
+ } elseif ($ def instanceof TypeDefinitionNode) {
96
+ $ typeDefinitionMap [$ def ->name ->value ] = $ def ;
97
+ } elseif ($ def instanceof TypeExtensionNode) {
98
+ $ extendedTypeName = $ def ->name ->value ;
99
+ $ existingType = $ schema ->getType ($ extendedTypeName );
100
+ if ($ existingType === null ) {
101
+ throw new Error ('Cannot extend type " ' . $ extendedTypeName . '" because it does not exist in the existing schema. ' , [$ def ]);
102
+ }
103
+
104
+ static ::assertTypeMatchesExtension ($ existingType , $ def );
105
+ static ::$ typeExtensionsMap [$ extendedTypeName ][] = $ def ;
106
+ } elseif ($ def instanceof DirectiveDefinitionNode) {
107
+ $ directiveName = $ def ->name ->value ;
108
+ $ existingDirective = $ schema ->getDirective ($ directiveName );
109
+ if ($ existingDirective !== null ) {
110
+ throw new Error ('Directive " ' . $ directiveName . '" already exists in the schema. It cannot be redefined. ' , [$ def ]);
111
+ }
112
+
113
+ $ directiveDefinitions [] = $ def ;
114
+ }
115
+ }
116
+
117
+ if (
118
+ count (static ::$ typeExtensionsMap ) === 0
119
+ && count ($ typeDefinitionMap ) === 0
120
+ && count ($ directiveDefinitions ) === 0
121
+ && count ($ schemaExtensions ) === 0
122
+ && $ schemaDef === null
123
+ ) {
124
+ return $ schema ;
125
+ }
126
+
127
+ static ::$ astBuilder = new ASTDefinitionBuilder (
128
+ $ typeDefinitionMap ,
129
+ static function (string $ typeName ) use ($ schema ): Type {
130
+ $ existingType = $ schema ->getType ($ typeName );
131
+ if ($ existingType === null ) {
132
+ throw new InvariantViolation ('Unknown type: " ' . $ typeName . '". ' );
133
+ }
134
+
135
+ return static ::extendNamedType ($ existingType );
136
+ },
137
+ $ typeConfigDecorator
138
+ );
139
+
140
+ static ::$ extendTypeCache = [];
141
+
142
+ $ operationTypes = [
143
+ 'query ' => static ::extendMaybeNamedType ($ schema ->getQueryType ()),
144
+ 'mutation ' => static ::extendMaybeNamedType ($ schema ->getMutationType ()),
145
+ 'subscription ' => static ::extendMaybeNamedType ($ schema ->getSubscriptionType ()),
146
+ ];
147
+
148
+ if ($ schemaDef !== null ) {
149
+ foreach ($ schemaDef ->operationTypes as $ operationType ) {
150
+ $ operationTypes [$ operationType ->operation ] = static ::$ astBuilder ->buildType ($ operationType ->type );
151
+ }
152
+ }
153
+
154
+ foreach ($ schemaExtensions as $ schemaExtension ) {
155
+ if (isset ($ schemaExtension ->operationTypes )) {
156
+ foreach ($ schemaExtension ->operationTypes as $ operationType ) {
157
+ $ operationTypes [$ operationType ->operation ] = static ::$ astBuilder ->buildType ($ operationType ->type );
158
+ }
159
+ }
160
+ }
161
+
162
+ $ schemaExtensionASTNodes = array_merge ($ schema ->extensionASTNodes , $ schemaExtensions );
163
+
164
+ $ types = [];
165
+ // Iterate through all types, getting the type definition for each, ensuring
166
+ // that any type not directly referenced by a field will get created.
167
+ foreach ($ schema ->getTypeMap () as $ type ) {
168
+ $ types [] = static ::extendNamedType ($ type );
169
+ }
170
+
171
+ // Do the same with new types.
172
+ foreach ($ typeDefinitionMap as $ type ) {
173
+ $ types [] = static ::$ astBuilder ->buildType ($ type );
174
+ }
175
+
176
+ return new Schema ([
177
+ 'query ' => $ operationTypes ['query ' ],
178
+ 'mutation ' => $ operationTypes ['mutation ' ],
179
+ 'subscription ' => $ operationTypes ['subscription ' ],
180
+ 'types ' => $ types ,
181
+ 'directives ' => static ::getMergedDirectives ($ schema , $ directiveDefinitions ),
182
+ 'astNode ' => $ schema ->getAstNode (),
183
+ 'extensionASTNodes ' => $ schemaExtensionASTNodes ,
184
+ ]);
185
+ }
186
+
59
187
/**
60
188
* @param Type&NamedType $type
61
189
*
@@ -541,132 +669,4 @@ protected static function extendDirective(Directive $directive): Directive
541
669
'astNode ' => $ directive ->astNode ,
542
670
]);
543
671
}
544
-
545
- /**
546
- * @param array<string, bool> $options
547
- * @phpstan-param TypeConfigDecorator|null $typeConfigDecorator
548
- */
549
- public static function extend (
550
- Schema $ schema ,
551
- DocumentNode $ documentAST ,
552
- array $ options = [],
553
- ?callable $ typeConfigDecorator = null
554
- ): Schema {
555
- if (
556
- ! ($ options ['assumeValid ' ] ?? false )
557
- && ! ($ options ['assumeValidSDL ' ] ?? false )
558
- ) {
559
- DocumentValidator::assertValidSDLExtension ($ documentAST , $ schema );
560
- }
561
-
562
- /** @var array<string, Node&TypeDefinitionNode> $typeDefinitionMap */
563
- $ typeDefinitionMap = [];
564
-
565
- static ::$ typeExtensionsMap = [];
566
-
567
- /** @var array<int, DirectiveDefinitionNode> $directiveDefinitions */
568
- $ directiveDefinitions = [];
569
-
570
- /** @var SchemaDefinitionNode|null $schemaDef */
571
- $ schemaDef = null ;
572
-
573
- /** @var array<int, SchemaExtensionNode> $schemaExtensions */
574
- $ schemaExtensions = [];
575
-
576
- foreach ($ documentAST ->definitions as $ def ) {
577
- if ($ def instanceof SchemaDefinitionNode) {
578
- $ schemaDef = $ def ;
579
- } elseif ($ def instanceof SchemaExtensionNode) {
580
- $ schemaExtensions [] = $ def ;
581
- } elseif ($ def instanceof TypeDefinitionNode) {
582
- $ typeDefinitionMap [$ def ->name ->value ] = $ def ;
583
- } elseif ($ def instanceof TypeExtensionNode) {
584
- $ extendedTypeName = $ def ->name ->value ;
585
- $ existingType = $ schema ->getType ($ extendedTypeName );
586
- if ($ existingType === null ) {
587
- throw new Error ('Cannot extend type " ' . $ extendedTypeName . '" because it does not exist in the existing schema. ' , [$ def ]);
588
- }
589
-
590
- static ::assertTypeMatchesExtension ($ existingType , $ def );
591
- static ::$ typeExtensionsMap [$ extendedTypeName ][] = $ def ;
592
- } elseif ($ def instanceof DirectiveDefinitionNode) {
593
- $ directiveName = $ def ->name ->value ;
594
- $ existingDirective = $ schema ->getDirective ($ directiveName );
595
- if ($ existingDirective !== null ) {
596
- throw new Error ('Directive " ' . $ directiveName . '" already exists in the schema. It cannot be redefined. ' , [$ def ]);
597
- }
598
-
599
- $ directiveDefinitions [] = $ def ;
600
- }
601
- }
602
-
603
- if (
604
- count (static ::$ typeExtensionsMap ) === 0
605
- && count ($ typeDefinitionMap ) === 0
606
- && count ($ directiveDefinitions ) === 0
607
- && count ($ schemaExtensions ) === 0
608
- && $ schemaDef === null
609
- ) {
610
- return $ schema ;
611
- }
612
-
613
- static ::$ astBuilder = new ASTDefinitionBuilder (
614
- $ typeDefinitionMap ,
615
- static function (string $ typeName ) use ($ schema ): Type {
616
- $ existingType = $ schema ->getType ($ typeName );
617
- if ($ existingType === null ) {
618
- throw new InvariantViolation ('Unknown type: " ' . $ typeName . '". ' );
619
- }
620
-
621
- return static ::extendNamedType ($ existingType );
622
- },
623
- $ typeConfigDecorator
624
- );
625
-
626
- static ::$ extendTypeCache = [];
627
-
628
- $ operationTypes = [
629
- 'query ' => static ::extendMaybeNamedType ($ schema ->getQueryType ()),
630
- 'mutation ' => static ::extendMaybeNamedType ($ schema ->getMutationType ()),
631
- 'subscription ' => static ::extendMaybeNamedType ($ schema ->getSubscriptionType ()),
632
- ];
633
-
634
- if ($ schemaDef !== null ) {
635
- foreach ($ schemaDef ->operationTypes as $ operationType ) {
636
- $ operationTypes [$ operationType ->operation ] = static ::$ astBuilder ->buildType ($ operationType ->type );
637
- }
638
- }
639
-
640
- foreach ($ schemaExtensions as $ schemaExtension ) {
641
- if (isset ($ schemaExtension ->operationTypes )) {
642
- foreach ($ schemaExtension ->operationTypes as $ operationType ) {
643
- $ operationTypes [$ operationType ->operation ] = static ::$ astBuilder ->buildType ($ operationType ->type );
644
- }
645
- }
646
- }
647
-
648
- $ schemaExtensionASTNodes = array_merge ($ schema ->extensionASTNodes , $ schemaExtensions );
649
-
650
- $ types = [];
651
- // Iterate through all types, getting the type definition for each, ensuring
652
- // that any type not directly referenced by a field will get created.
653
- foreach ($ schema ->getTypeMap () as $ type ) {
654
- $ types [] = static ::extendNamedType ($ type );
655
- }
656
-
657
- // Do the same with new types.
658
- foreach ($ typeDefinitionMap as $ type ) {
659
- $ types [] = static ::$ astBuilder ->buildType ($ type );
660
- }
661
-
662
- return new Schema ([
663
- 'query ' => $ operationTypes ['query ' ],
664
- 'mutation ' => $ operationTypes ['mutation ' ],
665
- 'subscription ' => $ operationTypes ['subscription ' ],
666
- 'types ' => $ types ,
667
- 'directives ' => static ::getMergedDirectives ($ schema , $ directiveDefinitions ),
668
- 'astNode ' => $ schema ->getAstNode (),
669
- 'extensionASTNodes ' => $ schemaExtensionASTNodes ,
670
- ]);
671
- }
672
672
}
0 commit comments