4040use ShipMonk \InputMapper \Compiler \Mapper \Object \MapDateTimeImmutable ;
4141use ShipMonk \InputMapper \Compiler \Mapper \Object \MapEnum ;
4242use ShipMonk \InputMapper \Compiler \Mapper \Object \MapObject ;
43+ use ShipMonk \InputMapper \Compiler \Mapper \Optional as OptionalAttribute ;
4344use ShipMonk \InputMapper \Compiler \Mapper \Scalar \MapBool ;
4445use ShipMonk \InputMapper \Compiler \Mapper \Scalar \MapFloat ;
4546use ShipMonk \InputMapper \Compiler \Mapper \Scalar \MapInt ;
4647use ShipMonk \InputMapper \Compiler \Mapper \Scalar \MapString ;
48+ use ShipMonk \InputMapper \Compiler \Mapper \UndefinedAwareMapperCompiler ;
4749use ShipMonk \InputMapper \Compiler \Mapper \Wrapper \ChainMapperCompiler ;
50+ use ShipMonk \InputMapper \Compiler \Mapper \Wrapper \MapDefaultValue ;
4851use ShipMonk \InputMapper \Compiler \Mapper \Wrapper \MapNullable ;
4952use ShipMonk \InputMapper \Compiler \Mapper \Wrapper \MapOptional ;
5053use ShipMonk \InputMapper \Compiler \Mapper \Wrapper \ValidatedMapperCompiler ;
@@ -74,6 +77,7 @@ class DefaultMapperCompilerFactory implements MapperCompilerFactory
7477
7578 final public const DELEGATE_OBJECT_MAPPING = 'delegateObjectMapping ' ;
7679 final public const GENERIC_PARAMETERS = 'genericParameters ' ;
80+ final public const DEFAULT_VALUE = 'defaultValue ' ;
7781
7882 /**
7983 * @param array<class-string, callable(class-string, array<string, mixed>): MapperCompiler> $mapperCompilerFactories
@@ -213,7 +217,7 @@ public function create(TypeNode $type, array $options = []): MapperCompiler
213217 }
214218
215219 if ($ isNullable && count ($ subTypesWithoutNull ) === 1 ) {
216- return new MapNullable ( $ this ->createInner ( $ subTypesWithoutNull [0 ], $ options) );
220+ return $ this ->create ( new NullableTypeNode ( $ subTypesWithoutNull [0 ]) , $ options );
217221 }
218222 }
219223
@@ -396,10 +400,20 @@ protected function createParameterMapperCompiler(
396400 default => new ChainMapperCompiler ($ mappers ),
397401 };
398402
403+ foreach ($ parameterReflection ->getAttributes (OptionalAttribute::class, ReflectionAttribute::IS_INSTANCEOF ) as $ attribute ) {
404+ $ mapper = new MapDefaultValue ($ mapper , $ attribute ->newInstance ()->default );
405+ }
406+
399407 if (!PhpDocTypeUtils::isSubTypeOf ($ mapper ->getOutputType (), $ type )) {
400408 throw CannotCreateMapperCompilerException::withIncompatibleMapperForMethodParameter ($ mapper , $ parameterReflection , $ type );
401409 }
402410
411+ if ($ mapper instanceof UndefinedAwareMapperCompiler) {
412+ if (!PhpDocTypeUtils::isSubTypeOf ($ mapper ->getDefaultValueType (), $ type )) {
413+ throw CannotCreateMapperCompilerException::withIncompatibleDefaultValueParameter ($ mapper , $ parameterReflection , $ type );
414+ }
415+ }
416+
403417 foreach ($ validators as $ validator ) {
404418 $ mapper = $ this ->addValidator ($ mapper , $ validator );
405419 }
@@ -419,6 +433,10 @@ protected function addValidator(
419433 return new ValidatedMapperCompiler ($ mapperCompiler , [$ validatorCompiler ]);
420434 }
421435
436+ if ($ mapperCompiler instanceof MapDefaultValue) {
437+ return new MapDefaultValue ($ this ->addValidator ($ mapperCompiler ->mapperCompiler , $ validatorCompiler ), $ mapperCompiler ->defaultValue );
438+ }
439+
422440 if ($ mapperCompiler instanceof MapNullable) {
423441 return new MapNullable ($ this ->addValidator ($ mapperCompiler ->innerMapperCompiler , $ validatorCompiler ));
424442 }
0 commit comments