@@ -80,7 +80,7 @@ public function testNonExistingFilterThrowsAnException(): void
8080 protected function getCustomFilter (
8181 array $ customFilter ,
8282 string $ token = 'customFilter ' ,
83- array $ acceptedTypes = ['string ' ]
83+ array $ acceptedTypes = ['string ' , ' null ' ]
8484 ): FilterInterface {
8585 return new class ($ customFilter , $ token , $ acceptedTypes ) implements FilterInterface {
8686 private $ customFilter ;
@@ -532,7 +532,7 @@ public function testMultipleTransformingFiltersAppliedToOnePropertyThrowsAnExcep
532532 new class () extends DateTimeFilter {
533533 public function getAcceptedTypes (): array
534534 {
535- return [DateTime::class];
535+ return [DateTime::class, ' null ' ];
536536 }
537537
538538 public function getToken (): string
@@ -590,11 +590,12 @@ public static function exceptionFilter(string $value): void
590590 }
591591
592592 /**
593- * @dataProvider namespaceDataProvider
593+ * @dataProvider implicitNullNamespaceDataProvider
594594 *
595+ * @param bool $implicitNull
595596 * @param string $namespace
596597 */
597- public function testTransformingToScalarType (string $ namespace )
598+ public function testTransformingToScalarType (bool $ implicitNull , string $ namespace )
598599 {
599600 $ className = $ this ->generateClassFromFile (
600601 'TransformingScalarFilter.json ' ,
@@ -609,7 +610,9 @@ public function testTransformingToScalarType(string $namespace)
609610 'binary ' ,
610611 ['integer ' ]
611612 )
612- )
613+ ),
614+ false ,
615+ $ implicitNull
613616 );
614617
615618 $ fqcn = $ namespace . $ className ;
@@ -621,6 +624,15 @@ public function testTransformingToScalarType(string $namespace)
621624
622625 $ this ->assertSame (['value ' => 11 ], $ object ->toArray ());
623626 $ this ->assertSame ('{"value":11} ' , $ object ->toJSON ());
627+
628+ $ this ->expectException (ErrorRegistryException::class);
629+ $ this ->expectExceptionMessage (
630+ $ implicitNull
631+ ? 'Filter binary is not compatible with property type NULL for property value '
632+ : 'Invalid type for value. Requires [string, int], got NULL '
633+ );
634+
635+ new $ fqcn (['value ' => null ]);
624636 }
625637
626638 public static function filterIntToBinary (int $ value ): string
@@ -637,7 +649,8 @@ public function testInvalidFilterChainWithTransformingFilterThrowsAnException():
637649 {
638650 $ this ->expectException (SchemaException::class);
639651 $ this ->expectExceptionMessage (
640- 'Filter trim is not compatible with transformed property type DateTime for property filteredProperty '
652+ 'Filter trim is not compatible with transformed property type ' .
653+ '[null, DateTime] for property filteredProperty '
641654 );
642655
643656 $ this ->generateClassFromFileTemplate ('FilterChain.json ' , ['["dateTime", "trim"] ' ], null , false );
@@ -654,7 +667,7 @@ public function testFilterChainWithTransformingFilter(): void
654667 $ this ->getCustomFilter (
655668 [self ::class, 'stripTimeFilter ' ],
656669 'stripTime ' ,
657- [DateTime::class]
670+ [DateTime::class, ' null ' ]
658671 )
659672 ),
660673 false
@@ -692,7 +705,7 @@ public function testFilterChainWithTransformingFilterOnMultiTypeProperty(
692705 $ this ->getCustomFilter (
693706 [self ::class, 'stripTimeFilter ' ],
694707 'stripTime ' ,
695- [DateTime::class]
708+ [DateTime::class, ' null ' ]
696709 )
697710 ),
698711 false ,
@@ -722,6 +735,27 @@ public function implicitNullNamespaceDataProvider(): array
722735 );
723736 }
724737
738+ public function testFilterChainWithIncompatibleFilterAfterTransformingFilterOnMultiTypeProperty (): void
739+ {
740+ $ this ->expectException (SchemaException::class);
741+ $ this ->expectExceptionMessage (
742+ 'Filter stripTime is not compatible with transformed ' .
743+ 'property type [null, DateTime] for property filteredProperty '
744+ );
745+
746+ $ this ->generateClassFromFile (
747+ 'FilterChainMultiType.json ' ,
748+ (new GeneratorConfiguration ())
749+ ->addFilter (
750+ $ this ->getCustomFilter (
751+ [self ::class, 'stripTimeFilterStrict ' ],
752+ 'stripTime ' ,
753+ [DateTime::class]
754+ )
755+ )
756+ );
757+ }
758+
725759 public function testFilterAfterTransformingFilterIsSkippedIfTransformingFilterFails (): void
726760 {
727761 $ this ->expectException (ErrorRegistryException::class);
@@ -736,7 +770,7 @@ public function testFilterAfterTransformingFilterIsSkippedIfTransformingFilterFa
736770 $ this ->getCustomFilter (
737771 [self ::class, 'exceptionFilter ' ],
738772 'stripTime ' ,
739- [DateTime::class]
773+ [DateTime::class, ' null ' ]
740774 )
741775 ),
742776 false
@@ -766,7 +800,7 @@ public function testFilterWhichAppliesToMultiTypePropertyPartiallyThrowsAnExcept
766800 $ this ->getCustomFilter (
767801 [self ::class, 'stripTimeFilter ' ],
768802 'stripTime ' ,
769- [DateTime::class]
803+ [DateTime::class, ' null ' ]
770804 )
771805 ),
772806 false
@@ -778,6 +812,11 @@ public static function stripTimeFilter(?DateTime $value): ?DateTime
778812 return $ value !== null ? $ value ->setTime (0 , 0 ) : null ;
779813 }
780814
815+ public static function stripTimeFilterStrict (DateTime $ value ): DateTime
816+ {
817+ return $ value ->setTime (0 , 0 );
818+ }
819+
781820 /**
782821 * @dataProvider arrayFilterDataProvider
783822 *
0 commit comments