55namespace TypeLang \Mapper \Mapping \Driver ;
66
77use Symfony \Component \ExpressionLanguage \ExpressionLanguage ;
8- use Symfony \ Component \ ExpressionLanguage \ ParsedExpression ;
9- use TypeLang \Mapper \Exception \ Definition \ PropertyTypeNotFoundException ;
10- use TypeLang \Mapper \Exception \ Definition \ TypeNotFoundException ;
11- use TypeLang \Mapper \Exception \ Environment \ ComposerPackageRequiredException ;
8+ use TypeLang \ Mapper \ Mapping \ Driver \ ArrayConfigDriver \ ClassConfigLoaderInterface ;
9+ use TypeLang \Mapper \Mapping \ Driver \ ArrayConfigDriver \ ErrorMessagePropertyConfigLoader ;
10+ use TypeLang \Mapper \Mapping \ Driver \ ArrayConfigDriver \ NamePropertyConfigLoader ;
11+ use TypeLang \Mapper \Mapping \ Driver \ ArrayConfigDriver \ PropertyConfigLoaderInterface ;
1212use TypeLang \Mapper \Mapping \Driver \ArrayConfigDriver \SchemaValidator ;
13+ use TypeLang \Mapper \Mapping \Driver \ArrayConfigDriver \SkipConditionsPropertyConfigLoader ;
14+ use TypeLang \Mapper \Mapping \Driver \ArrayConfigDriver \TypePropertyConfigLoader ;
15+ use TypeLang \Mapper \Mapping \Driver \AttributeDriver \DiscriminatorMapClassConfigLoader ;
16+ use TypeLang \Mapper \Mapping \Driver \AttributeDriver \NormalizeAsArrayClassConfigLoader ;
1317use TypeLang \Mapper \Mapping \Metadata \ClassMetadata ;
14- use TypeLang \Mapper \Mapping \Metadata \DiscriminatorMapMetadata ;
15- use TypeLang \Mapper \Mapping \Metadata \EmptyConditionMetadata ;
16- use TypeLang \Mapper \Mapping \Metadata \ExpressionConditionMetadata ;
17- use TypeLang \Mapper \Mapping \Metadata \NullConditionMetadata ;
18- use TypeLang \Mapper \Mapping \Metadata \TypeMetadata ;
1918use TypeLang \Mapper \Runtime \Parser \TypeParserInterface ;
2019use TypeLang \Mapper \Runtime \Repository \TypeRepositoryInterface ;
2120
2827 * undefined_error_message?: non-empty-string,
2928 * ...
3029 * }
31- *
3230 * @phpstan-type ClassDiscriminatorConfigType array{
3331 * field: non-empty-string,
3432 * map: array<non-empty-string, non-empty-string>,
3533 * otherwise?: non-empty-string,
3634 * }
37- *
3835 * @phpstan-type ClassConfigType array{
3936 * normalize_as_array?: bool,
4037 * discriminator?: ClassDiscriminatorConfigType,
@@ -45,15 +42,52 @@ abstract class ArrayConfigDriver extends LoadableDriver
4542{
4643 private static ?bool $ supportsSchemaValidation = null ;
4744
45+ /**
46+ * @var list<ClassConfigLoaderInterface>
47+ */
48+ private readonly array $ classConfigLoaders ;
49+
50+ /**
51+ * @var list<PropertyConfigLoaderInterface>
52+ */
53+ private readonly array $ propertyConfigLoaders ;
54+
4855 public function __construct (
4956 DriverInterface $ delegate = new NullDriver (),
50- private ?ExpressionLanguage $ expression = null ,
57+ private readonly ?ExpressionLanguage $ expression = null ,
5158 ) {
5259 self ::$ supportsSchemaValidation ??= SchemaValidator::isAvailable ();
5360
61+ $ this ->classConfigLoaders = $ this ->createClassLoaders ();
62+ $ this ->propertyConfigLoaders = $ this ->createPropertyLoaders ();
63+
5464 parent ::__construct ($ delegate );
5565 }
5666
67+ /**
68+ * @return list<ClassConfigLoaderInterface>
69+ */
70+ private function createClassLoaders (): array
71+ {
72+ return [
73+ new NormalizeAsArrayClassConfigLoader (),
74+ new DiscriminatorMapClassConfigLoader (),
75+ ];
76+ }
77+
78+ /**
79+ * @return list<PropertyConfigLoaderInterface>
80+ */
81+ private function createPropertyLoaders (): array
82+ {
83+ return [
84+ new TypePropertyConfigLoader (),
85+ new NamePropertyConfigLoader (),
86+ new ErrorMessagePropertyConfigLoader (),
87+ new SkipConditionsPropertyConfigLoader ($ this ->expression ),
88+ ];
89+ }
90+
5791 /**
5892 * @param \ReflectionClass<object> $class
5993 *
@@ -109,82 +143,22 @@ protected function load(
109143 return ;
110144 }
111145
112- // ---------------------------------------------------------------------
113- // > start: Normalize as array
114- // ---------------------------------------------------------------------
115-
116- if (\array_key_exists ('normalize_as_array ' , $ classConfig )) {
117- // @phpstan-ignore-next-line : Additional DbC invariant
118- assert (\is_bool ($ classConfig ['normalize_as_array ' ]));
119-
120- $ class ->isNormalizeAsArray = $ classConfig ['normalize_as_array ' ];
121- }
122-
123- // ---------------------------------------------------------------------
124- // end: Normalize as array
125- // > start: Discriminator Map
126- // ---------------------------------------------------------------------
127-
128- if (\array_key_exists ('discriminator ' , $ classConfig )) {
129- // @phpstan-ignore-next-line : Additional DbC invariant
130- assert (\is_array ($ classConfig ['discriminator ' ]));
131-
132- $ discriminatorConfig = $ classConfig ['discriminator ' ];
133-
134- // @phpstan-ignore-next-line : Additional DbC invariant
135- assert (\array_key_exists ('field ' , $ discriminatorConfig ));
136-
137- $ discriminator = new DiscriminatorMapMetadata (
138- field: $ discriminatorConfig ['field ' ],
139- );
140-
141- // @phpstan-ignore-next-line : Additional DbC invariant
142- assert (\array_key_exists ('map ' , $ discriminatorConfig ));
143-
144- // @phpstan-ignore-next-line : Additional DbC invariant
145- assert (\is_array ($ discriminatorConfig ['map ' ]));
146-
147- foreach ($ discriminatorConfig ['map ' ] as $ discriminatorValue => $ discriminatorType ) {
148- // @phpstan-ignore-next-line : Additional DbC invariant
149- assert (\is_string ($ discriminatorValue ));
150-
151- // @phpstan-ignore-next-line : Additional DbC invariant
152- assert (\is_string ($ discriminatorType ));
153-
154- $ discriminator ->addType (
155- fieldValue: $ discriminatorValue ,
156- type: $ this ->createDiscriminatorType (
157- type: $ discriminatorType ,
158- class: $ reflection ,
159- types: $ types ,
160- parser: $ parser ,
161- ),
162- );
163- }
164-
165- // @phpstan-ignore-next-line : Additional DbC invariant
166- assert (\array_key_exists ('otherwise ' , $ discriminatorConfig ));
167-
168- // @phpstan-ignore-next-line : Additional DbC invariant
169- assert (\is_string ($ discriminatorConfig ['otherwise ' ]));
170-
171- $ discriminator ->default = $ this ->createDiscriminatorType (
172- type: $ discriminatorConfig ['otherwise ' ],
146+ foreach ($ this ->classConfigLoaders as $ classConfigLoader ) {
147+ $ classConfigLoader ->load (
148+ config: $ classConfig ,
173149 class: $ reflection ,
150+ metadata: $ class ,
174151 types: $ types ,
175152 parser: $ parser ,
176153 );
177154 }
178155
179- // ---------------------------------------------------------------------
180- // end: Discriminator Map
181- // > start: Properties
182- // ---------------------------------------------------------------------
156+ $ classConfig ['properties ' ] ??= [];
183157
184158 // @phpstan-ignore-next-line : Additional DbC invariant
185- assert (\is_array ($ classConfig ['properties ' ] ?? [] ));
159+ assert (\is_array ($ classConfig ['properties ' ]));
186160
187- foreach ($ classConfig ['properties ' ] ?? [] as $ propertyName => $ propertyConfig ) {
161+ foreach ($ classConfig ['properties ' ] as $ propertyName => $ propertyConfig ) {
188162 // Prepare: Normalize config in case of config contains string
189163 if (\is_string ($ propertyConfig )) {
190164 $ propertyConfig = ['type ' => $ propertyConfig ];
@@ -198,170 +172,15 @@ class: $reflection,
198172
199173 $ metadata = $ class ->getPropertyOrCreate ($ propertyName );
200174
201- // ---------------------------------------------------------------------
202- // start: Property Error Message
203- // ---------------------------------------------------------------------
204-
205- if (\array_key_exists ('type_error_message ' , $ propertyConfig )) {
206- // @phpstan-ignore-next-line : Additional DbC invariant
207- assert (\is_string ($ propertyConfig ['type_error_message ' ]));
208-
209- $ metadata ->typeErrorMessage = $ propertyConfig ['type_error_message ' ];
210- }
211-
212- if (\array_key_exists ('undefined_error_message ' , $ propertyConfig )) {
213- // @phpstan-ignore-next-line : Additional DbC invariant
214- assert (\is_string ($ propertyConfig ['undefined_error_message ' ]));
215-
216- $ metadata ->undefinedErrorMessage = $ propertyConfig ['undefined_error_message ' ];
217- }
218-
219- // -----------------------------------------------------------------
220- // start: Property Type
221- // -----------------------------------------------------------------
222-
223- if (\array_key_exists ('type ' , $ propertyConfig )) {
224- // @phpstan-ignore-next-line : Additional DbC invariant
225- assert (\is_string ($ propertyConfig ['type ' ]));
226-
227- $ metadata ->type = $ this ->createPropertyType (
228- class: $ reflection ,
229- propertyName: $ propertyName ,
230- propertyType: $ propertyConfig ['type ' ],
175+ foreach ($ this ->propertyConfigLoaders as $ propertyConfigLoader ) {
176+ $ propertyConfigLoader ->load (
177+ config: $ propertyConfig ,
178+ property: new \ReflectionProperty ($ class , $ propertyName ),
179+ metadata: $ metadata ,
231180 types: $ types ,
232181 parser: $ parser ,
233182 );
234183 }
235-
236- // -----------------------------------------------------------------
237- // end: Property Type
238- // > start: Property Name
239- // -----------------------------------------------------------------
240-
241- if (\array_key_exists ('name ' , $ propertyConfig )) {
242- // @phpstan-ignore-next-line : Additional DbC invariant
243- assert (\is_string ($ propertyConfig ['name ' ]));
244-
245- $ metadata ->alias = $ propertyConfig ['name ' ];
246- }
247-
248- // -----------------------------------------------------------------
249- // end: Property Name
250- // > start: Property Skip Behaviour
251- // -----------------------------------------------------------------
252-
253- if (\array_key_exists ('skip ' , $ propertyConfig )) {
254- if (\is_string ($ propertyConfig ['skip ' ])) {
255- $ propertyConfig ['skip ' ] = [$ propertyConfig ['skip ' ]];
256- }
257-
258- // @phpstan-ignore-next-line : Additional DbC invariant
259- assert (\is_array ($ propertyConfig ['skip ' ]));
260-
261- foreach ($ propertyConfig ['skip ' ] as $ propertyConfigSkip ) {
262- // @phpstan-ignore-next-line : Additional DbC invariant
263- assert (\is_string ($ propertyConfigSkip ));
264-
265- $ metadata ->addSkipCondition (match ($ propertyConfigSkip ) {
266- 'null ' => new NullConditionMetadata (),
267- 'empty ' => new EmptyConditionMetadata (),
268- default => new ExpressionConditionMetadata (
269- expression: $ this ->createExpression ($ propertyConfigSkip , [
270- ExpressionConditionMetadata::DEFAULT_CONTEXT_VARIABLE_NAME ,
271- ]),
272- variable: ExpressionConditionMetadata::DEFAULT_CONTEXT_VARIABLE_NAME ,
273- )
274- });
275- }
276- }
277-
278- // -----------------------------------------------------------------
279- // end: Property Skip Behaviour
280- // -----------------------------------------------------------------
281- }
282-
283- // ---------------------------------------------------------------------
284- // end: Properties
285- // ---------------------------------------------------------------------
286- }
287-
288- /**
289- * @param \ReflectionClass<object> $class
290- * @param non-empty-string $propertyName
291- * @param non-empty-string $propertyType
292- *
293- * @throws PropertyTypeNotFoundException in case of property type not found
294- * @throws \Throwable in case of internal error occurs
295- */
296- private function createPropertyType (
297- \ReflectionClass $ class ,
298- string $ propertyName ,
299- string $ propertyType ,
300- TypeRepositoryInterface $ types ,
301- TypeParserInterface $ parser ,
302- ): TypeMetadata {
303- $ statement = $ parser ->getStatementByDefinition ($ propertyType );
304-
305- try {
306- $ instance = $ types ->getTypeByStatement ($ statement , $ class );
307- } catch (TypeNotFoundException $ e ) {
308- throw PropertyTypeNotFoundException::becauseTypeOfPropertyNotDefined (
309- class: $ class ->getName (),
310- property: $ propertyName ,
311- type: $ e ->getType (),
312- previous: $ e ,
313- );
314- }
315-
316- return new TypeMetadata ($ instance , $ statement );
317- }
318-
319- /**
320- * @param non-empty-string $type
321- * @param \ReflectionClass<object> $class
322- *
323- * @throws PropertyTypeNotFoundException
324- * @throws \Throwable
325- */
326- private function createDiscriminatorType (
327- string $ type ,
328- \ReflectionClass $ class ,
329- TypeRepositoryInterface $ types ,
330- TypeParserInterface $ parser ,
331- ): TypeMetadata {
332- $ statement = $ parser ->getStatementByDefinition ($ type );
333-
334- // TODO Add custom "discriminator type exception"
335- $ instance = $ types ->getTypeByStatement ($ statement , $ class );
336-
337- return new TypeMetadata ($ instance , $ statement );
338- }
339-
340- /**
341- * @param non-empty-string $expression
342- * @param list<non-empty-string> $names
343- *
344- * @throws ComposerPackageRequiredException
345- */
346- private function createExpression (string $ expression , array $ names ): ParsedExpression
347- {
348- $ parser = ($ this ->expression ??= $ this ->createDefaultExpressionLanguage ());
349-
350- return $ parser ->parse ($ expression , $ names );
351- }
352-
353- /**
354- * @throws ComposerPackageRequiredException
355- */
356- private function createDefaultExpressionLanguage (): ExpressionLanguage
357- {
358- if (!\class_exists (ExpressionLanguage::class)) {
359- throw ComposerPackageRequiredException::becausePackageNotInstalled (
360- package: 'symfony/expression-language ' ,
361- purpose: 'expressions support ' ,
362- );
363184 }
364-
365- return new ExpressionLanguage ();
366185 }
367186}
0 commit comments