2020use Symfony \Component \Config \Definition \NodeInterface ;
2121use Symfony \Component \Config \Definition \NumericNode ;
2222use Symfony \Component \Config \Definition \PrototypedArrayNode ;
23- use Symfony \Component \Config \Definition \ScalarNode ;
2423use Symfony \Component \Config \Definition \StringNode ;
2524use Symfony \Component \Config \Definition \VariableNode ;
2625
2726/**
2827 * Generates a JSON Schema from a Node definition.
2928 *
30- * @internal
29+ * @author Jérôme Tamarelle<jerome@tamarelle.net>
3130 */
3231final class JsonSchemaGenerator
3332{
@@ -36,109 +35,107 @@ final class JsonSchemaGenerator
3635 *
3736 * @param array<string, mixed> $meta Additional attributes to include in the schema root
3837 */
39- public function generate (NodeInterface $ node ,array $ meta = []):\ stdClass
38+ public function generate (NodeInterface $ node ,array $ meta = []):array
4039 {
41- return ( object ) [
40+ return [
4241'$schema ' =>'http://json-schema.org/draft-07/schema ' ,
4342 ...$ meta ,
44- ...( array ) $ this ->generateNode ($ node ),
43+ ...$ this ->generateNode ($ node ),
4544 ];
4645 }
4746
48- public function generateNode (NodeInterface $ node ):\ stdClass
47+ public function generateNode (NodeInterface $ node ):array
4948 {
5049if ($ nodeinstanceof PrototypedArrayNode) {
5150$ prototypeSchema =$ this ->generateNode ($ node ->getPrototype ());
5251
5352if ($ node ->getKeyAttribute ()) {
54- $ schema =( object ) [
53+ $ schema = [
5554'type ' =>'object ' ,
5655'additionalProperties ' =>$ prototypeSchema ,
5756 ];
5857
5958if ($ node ->getMinNumberOfElements ()) {
60- $ schema-> minProperties =$ node ->getMinNumberOfElements ();
59+ $ schema[ ' minProperties ' ] =$ node ->getMinNumberOfElements ();
6160 }
6261 }else {
63- $ schema =( object ) [
62+ $ schema = [
6463'type ' =>'array ' ,
6564'items ' =>$ prototypeSchema
6665 ];
6766
6867if ($ node ->getMinNumberOfElements ()) {
69- $ schema-> minItems =$ node ->getMinNumberOfElements ();
68+ $ schema[ ' minItems ' ] =$ node ->getMinNumberOfElements ();
7069 }
7170 }
7271 }elseif ($ nodeinstanceof ArrayNode) {
73- $ schema =( object ) ['type ' => ['object ' ]];
72+ $ schema = ['type ' => ['object ' ]];
7473$ children =$ node ->getChildren ();
7574foreach ($ childrenas $ child ) {
76- $ schema-> properties ??=new \ stdClass () ;
77- $ schema-> properties ->{ $ child ->getName ()} =$ this ->generateNode ($ child );
75+ $ schema[ ' properties ' ] ??=[] ;
76+ $ schema[ ' properties ' ][ $ child ->getName ()] =$ this ->generateNode ($ child );
7877if ($ child ->isRequired ()) {
79- $ schema-> required ??= [];
80- $ schema-> required [] =$ child ->getName ();
78+ $ schema[ ' required ' ] ??= [];
79+ $ schema[ ' required ' ] [] =$ child ->getName ();
8180 }
8281 }
8382
84- if (property_exists ($ schema, 'properties ' )) {
83+ if (isset ($ schema[ 'properties ' ] )) {
8584if ($ node ->shouldIgnoreExtraKeys ()) {
86- $ schema-> additionalProperties =true ;
85+ $ schema[ ' additionalProperties ' ] =true ;
8786 }else {
88- $ schema-> additionalProperties =false ;
87+ $ schema[ ' additionalProperties ' ] =false ;
8988 }
9089 }
9190 }elseif ($ nodeinstanceof BooleanNode) {
92- $ schema =( object ) ['type ' => ['boolean ' ]];
91+ $ schema = ['type ' => ['boolean ' ]];
9392
9493if ($ node ->isNullable ()) {
95- $ schema-> type [] ='null ' ;
94+ $ schema[ ' type ' ] [] ='null ' ;
9695 }
9796 }elseif ($ nodeinstanceof StringNode) {
98- $ schema =( object ) ['type ' => ['string ' ]];
97+ $ schema = ['type ' => ['string ' ]];
9998 }elseif ($ nodeinstanceof EnumNode) {
100- $ schema =( object ) [
99+ $ schema = [
101100'$anyOf ' => [
102- ( object ) ['enum ' =>array_map (static function (mixed $ case ):string |int |float |bool |null {
101+ ['enum ' =>array_map (static function (mixed $ case ):string |int |float |bool |null {
103102if ($ caseinstanceof \UnitEnum) {
104103return \sprintf ('!php/enum %s::%s ' ,$ case ::class,$ case ->name );
105104 }
106105
107106return $ case ;
108107 },$ node ->getValues ())],
109- ( object ) ['type ' =>'null ' ],
108+ ['type ' =>'null ' ],
110109 ],
111110 ];
112111 }elseif ($ nodeinstanceof NumericNode) {
113- $ schema =( object ) ['type ' =>$ nodeinstanceof IntegerNode ? ['integer ' ] : ['number ' ]];
112+ $ schema = ['type ' =>$ nodeinstanceof IntegerNode ? ['integer ' ] : ['number ' ]];
114113
115114if (null !==$ node ->getMin ()) {
116- $ schema-> minimum =$ node ->getMin ();
115+ $ schema[ ' minimum ' ] =$ node ->getMin ();
117116 }
118117
119118if (null !==$ node ->getMax ()) {
120- $ schema-> maximum =$ node ->getMax ();
119+ $ schema[ ' maximum ' ] =$ node ->getMax ();
121120 }
122- }elseif ($ nodeinstanceof ScalarNode) {
123- $ schema = (object ) ['type ' => ['array ' ,'object ' ,'string ' ,'number ' ,'boolean ' ,'null ' ]];
124121 }elseif ($ nodeinstanceof VariableNode) {
125- $ schema =( object ) ['type ' =>null ];
122+ $ schema = ['type ' =>[ ' array ' , ' object ' , ' string ' , ' number ' , ' boolean ' , ' null ' ] ];
126123 }else {
127- $ schema =( object ) ['type ' => ['null ' ]];
124+ $ schema = ['type ' => ['null ' ]];
128125 }
129126
130127if ($ nodeinstanceof BaseNode) {
131128$ normalizedTypes =array_diff ($ node ->getNormalizedTypes (), [ExprBuilder::TYPE_ANY , ExprBuilder::TYPE_ARRAY ]);
132129
133- if (isset ($ schema-> type ) &&$ node ->hasDefaultValue () &&$ node ->getDefaultValue () ===null ) {
130+ if (isset ($ schema[ ' type ' ] ) &&$ node ->hasDefaultValue () &&$ node ->getDefaultValue () ===null ) {
134131$ normalizedTypes [] ='null ' ;
135132 }
136133
137134if ($ normalizedTypes ) {
138- $ schema =( object ) [
135+ $ schema = [
139136'$anyOf ' => [
140137$ schema ,
141- ( object ) [
138+ [
142139'type ' =>array_values (
143140array_unique (
144141array_map (fn ($ type ) =>match ($ type ) {
@@ -156,32 +153,32 @@ public function generateNode(NodeInterface $node): \stdClass
156153 ];
157154 }
158155
159- if (is_array ($ schema-> type ??null )) {
160- $ schema-> type =array_values (array_unique ($ schema-> type ));
156+ if (is_array ($ schema[ ' type ' ] ??null )) {
157+ $ schema[ ' type ' ] =array_values (array_unique ($ schema[ ' type ' ] ));
161158 }
162159
163160if ($ node ->hasDefaultValue () && !($ nodeinstanceof ArrayNode &&$ node ->getDefaultValue () === [])) {
164- $ schema-> default =$ node ->getDefaultValue ();
161+ $ schema[ ' default ' ] =$ node ->getDefaultValue ();
165162 }
166163
167164if ($ node ->getInfo ()) {
168- $ schema-> description =$ node ->getInfo ();
165+ $ schema[ ' description ' ] =$ node ->getInfo ();
169166 }
170167
171168if ($ node ->getExample ()) {
172- $ schema-> examples = [$ node ->getExample ()];
169+ $ schema[ ' examples ' ] = [$ node ->getExample ()];
173170 }
174171
175172if ($ node ->isDeprecated ()) {
176- $ schema-> deprecated =true ;
177- $ schema-> deprecationMessage =$ node ->getDeprecationMessage ($ node );
173+ $ schema[ ' deprecated ' ] =true ;
174+ $ schema[ ' deprecationMessage ' ] =$ node ->getDeprecationMessage ($ node );
178175 }
179176 }
180177
181- if (empty ($ schema-> type )) {
182- unset($ schema-> type );
183- }elseif (is_array ($ schema-> type ) &&count ($ schema-> type ) ===1 ) {
184- $ schema-> type =$ schema-> type [0 ];
178+ if (empty ($ schema[ ' type ' ] )) {
179+ unset($ schema[ ' type ' ] );
180+ }elseif (is_array ($ schema[ ' type ' ] ) &&count ($ schema[ ' type ' ] ) ===1 ) {
181+ $ schema[ ' type ' ] =$ schema[ ' type ' ] [0 ];
185182 }
186183
187184return $ schema ;