From aa805d6e942f320262d884683768db68e98b6947 Mon Sep 17 00:00:00 2001 From: Robertbaelde Date: Sun, 31 Mar 2024 13:39:39 +0200 Subject: [PATCH 1/2] Cast to expected type --- src/Fixtures/CastEmptyStringToNull.php | 2 +- .../CastToClassWithStaticConstructor.php | 2 +- src/Fixtures/CastToExpectedType/AuthorId.php | 19 ++++++++++++++++++ src/Fixtures/CastToExpectedType/BlogId.php | 19 ++++++++++++++++++ .../CastToExpectedType/ClassWithIds.php | 15 ++++++++++++++ src/Fixtures/CastToExpectedType/IdCaster.php | 19 ++++++++++++++++++ src/Fixtures/CastToLowerCase.php | 2 +- src/ObjectHydrationTestCase.php | 20 +++++++++++++++++++ src/ObjectMapperCodeGenerator.php | 2 +- src/ObjectMapperUsingReflection.php | 6 ++++-- src/ObjectSerializationTestCase.php | 1 + src/PropertyCaster.php | 2 +- src/PropertyCasters/CastListToType.php | 2 +- src/PropertyCasters/CastToArrayWithKey.php | 2 +- .../CastToDateTimeImmutable.php | 2 +- src/PropertyCasters/CastToType.php | 2 +- src/PropertyCasters/CastToUuid.php | 2 +- 17 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 src/Fixtures/CastToExpectedType/AuthorId.php create mode 100644 src/Fixtures/CastToExpectedType/BlogId.php create mode 100644 src/Fixtures/CastToExpectedType/ClassWithIds.php create mode 100644 src/Fixtures/CastToExpectedType/IdCaster.php diff --git a/src/Fixtures/CastEmptyStringToNull.php b/src/Fixtures/CastEmptyStringToNull.php index 027540d..f815f6f 100644 --- a/src/Fixtures/CastEmptyStringToNull.php +++ b/src/Fixtures/CastEmptyStringToNull.php @@ -9,7 +9,7 @@ #[Attribute(Attribute::TARGET_PROPERTY)] final class CastEmptyStringToNull implements PropertyCaster { - public function cast(mixed $value, ObjectMapper $hydrator): mixed + public function cast(mixed $value, ObjectMapper $hydrator, ?string $expectedTypeName): mixed { if ($value === '') { return null; diff --git a/src/Fixtures/CastToClassWithStaticConstructor.php b/src/Fixtures/CastToClassWithStaticConstructor.php index c00a048..c2e2440 100644 --- a/src/Fixtures/CastToClassWithStaticConstructor.php +++ b/src/Fixtures/CastToClassWithStaticConstructor.php @@ -11,7 +11,7 @@ #[Attribute(Attribute::TARGET_PARAMETER)] final class CastToClassWithStaticConstructor implements PropertyCaster { - public function cast(mixed $value, ObjectMapper $hydrator): mixed + public function cast(mixed $value, ObjectMapper $hydrator, ?string $expectedTypeName): mixed { return $hydrator->hydrateObject(ClassWithStaticConstructor::class, ['name' => $value]); } diff --git a/src/Fixtures/CastToExpectedType/AuthorId.php b/src/Fixtures/CastToExpectedType/AuthorId.php new file mode 100644 index 0000000..141612c --- /dev/null +++ b/src/Fixtures/CastToExpectedType/AuthorId.php @@ -0,0 +1,19 @@ +id; + } +} diff --git a/src/Fixtures/CastToExpectedType/BlogId.php b/src/Fixtures/CastToExpectedType/BlogId.php new file mode 100644 index 0000000..97de6ea --- /dev/null +++ b/src/Fixtures/CastToExpectedType/BlogId.php @@ -0,0 +1,19 @@ +id; + } +} diff --git a/src/Fixtures/CastToExpectedType/ClassWithIds.php b/src/Fixtures/CastToExpectedType/ClassWithIds.php new file mode 100644 index 0000000..a30a5a3 --- /dev/null +++ b/src/Fixtures/CastToExpectedType/ClassWithIds.php @@ -0,0 +1,15 @@ +createObjectHydrator(); + $payload = [ + 'authorId' => 'a', + 'blogId' => 'b', + ]; + + $object = $hydrator->hydrateObject(ClassWithIds::class, $payload); + + self::assertInstanceOf(ClassWithIds::class, $object); + self::assertInstanceOf(AuthorId::class, $object->authorId); + self::assertInstanceOf(BlogId::class, $object->blogId); + } + /** * @test * @see https://github.com/EventSaucePHP/ObjectHydrator/issues/56 diff --git a/src/ObjectMapperCodeGenerator.php b/src/ObjectMapperCodeGenerator.php index cab9471..69cd9e7 100644 --- a/src/ObjectMapperCodeGenerator.php +++ b/src/ObjectMapperCodeGenerator.php @@ -294,7 +294,7 @@ private function dumpClassHydrator(string $className, ClassHydrationDefinition $ \$$casterName = new \\$caster(...$casterOptions); } - \$value = \${$casterName}->cast(\$value, \$this); + \$value = \${$casterName}->cast(\$value, \$this, '$definition->firstTypeName'); if (\$value === null) { $isNullBody diff --git a/src/ObjectMapperUsingReflection.php b/src/ObjectMapperUsingReflection.php index a9d09fd..e02796a 100644 --- a/src/ObjectMapperUsingReflection.php +++ b/src/ObjectMapperUsingReflection.php @@ -110,11 +110,13 @@ public function hydrateObject(string $className, array $payload): object continue; } + $typeName = $definition->firstTypeName; + foreach ($definition->casters as [$caster, $options]) { $key = $className . '-' . $caster . '-' . json_encode($options); /** @var PropertyCaster $propertyCaster */ $propertyCaster = $this->casterInstances[$key] ??= new $caster(...$options); - $value = $propertyCaster->cast($value, $this); + $value = $propertyCaster->cast($value, $this, $typeName); } // same code as two sections above @@ -133,7 +135,7 @@ public function hydrateObject(string $className, array $payload): object $value = $this->hydrateViaTypeMap($definition, $value); } - $typeName = $definition->firstTypeName; + if ($definition->isBackedEnum()) { $value = $typeName::from($value); diff --git a/src/ObjectSerializationTestCase.php b/src/ObjectSerializationTestCase.php index 94a758d..3886617 100644 --- a/src/ObjectSerializationTestCase.php +++ b/src/ObjectSerializationTestCase.php @@ -8,6 +8,7 @@ use DateTimeImmutable; use EventSauce\ObjectHydrator\Fixtures\CastersOnClasses\ClassWithClassLevelMapFrom; use EventSauce\ObjectHydrator\Fixtures\CastersOnClasses\ClassWithClassLevelMapFromMultiple; +use EventSauce\ObjectHydrator\Fixtures\CastToExpectedType\ClassWithIds; use EventSauce\ObjectHydrator\Fixtures\ClassReferencedByUnionOne; use EventSauce\ObjectHydrator\Fixtures\ClassReferencedByUnionTwo; use EventSauce\ObjectHydrator\Fixtures\ClassThatOmitsPublicMethods; diff --git a/src/PropertyCaster.php b/src/PropertyCaster.php index f15e1aa..af0695e 100644 --- a/src/PropertyCaster.php +++ b/src/PropertyCaster.php @@ -6,5 +6,5 @@ interface PropertyCaster { - public function cast(mixed $value, ObjectMapper $hydrator): mixed; + public function cast(mixed $value, ObjectMapper $hydrator, ?string $expectedTypeName): mixed; } diff --git a/src/PropertyCasters/CastListToType.php b/src/PropertyCasters/CastListToType.php index 8a7db55..9c6663b 100644 --- a/src/PropertyCasters/CastListToType.php +++ b/src/PropertyCasters/CastListToType.php @@ -31,7 +31,7 @@ public function __construct( $this->isEnum = enum_exists($this->propertyType); } - public function cast(mixed $value, ObjectMapper $hydrator): mixed + public function cast(mixed $value, ObjectMapper $hydrator, ?string $expectedTypeName): mixed { assert(is_array($value), 'value is expected to be an array'); diff --git a/src/PropertyCasters/CastToArrayWithKey.php b/src/PropertyCasters/CastToArrayWithKey.php index 973dca4..46dc3c2 100644 --- a/src/PropertyCasters/CastToArrayWithKey.php +++ b/src/PropertyCasters/CastToArrayWithKey.php @@ -17,7 +17,7 @@ public function __construct(private string $key) { } - public function cast(mixed $value, ObjectMapper $hydrator): mixed + public function cast(mixed $value, ObjectMapper $hydrator, ?string $expectedTypeName): mixed { return [$this->key => $value]; } diff --git a/src/PropertyCasters/CastToDateTimeImmutable.php b/src/PropertyCasters/CastToDateTimeImmutable.php index ce508e5..4de18f9 100644 --- a/src/PropertyCasters/CastToDateTimeImmutable.php +++ b/src/PropertyCasters/CastToDateTimeImmutable.php @@ -21,7 +21,7 @@ public function __construct(private ?string $format = null, private ?string $tim { } - public function cast(mixed $value, ObjectMapper $hydrator): mixed + public function cast(mixed $value, ObjectMapper $hydrator, ?string $expectedTypeName): mixed { $timeZone = $this->timeZone ? new DateTimeZone($this->timeZone) : $this->timeZone; diff --git a/src/PropertyCasters/CastToType.php b/src/PropertyCasters/CastToType.php index 8349676..ec004f4 100644 --- a/src/PropertyCasters/CastToType.php +++ b/src/PropertyCasters/CastToType.php @@ -19,7 +19,7 @@ public function __construct( ) { } - public function cast(mixed $value, ObjectMapper $hydrator): mixed + public function cast(mixed $value, ObjectMapper $hydrator, ?string $expectedTypeName): mixed { settype($value, $this->propertyType); diff --git a/src/PropertyCasters/CastToUuid.php b/src/PropertyCasters/CastToUuid.php index 8cfbad8..fd166ba 100644 --- a/src/PropertyCasters/CastToUuid.php +++ b/src/PropertyCasters/CastToUuid.php @@ -20,7 +20,7 @@ public function __construct(private string $type = 'string') { } - public function cast(mixed $value, ObjectMapper $hydrator): UuidInterface + public function cast(mixed $value, ObjectMapper $hydrator, ?string $expectedTypeName): UuidInterface { $value = (string) $value; From 67eee04c2eb53c61adcae4df304a2521b11265eb Mon Sep 17 00:00:00 2001 From: Robertbaelde Date: Sun, 31 Mar 2024 13:43:02 +0200 Subject: [PATCH 2/2] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f6404d..8c874f6 100644 --- a/README.md +++ b/README.md @@ -401,7 +401,7 @@ class CastToMoney implements PropertyCaster private string $currency ) {} - public function cast(mixed $value, ObjectMapper $mapper) : mixed + public function cast(mixed $value, ObjectMapper $mapper, ?string $expectedTypeName) : mixed { return new Money($value, Currency::fromString($this->currency)); } @@ -416,7 +416,7 @@ class CastUnionToType implements PropertyCaster private array $typeToClassMap ) {} - public function cast(mixed $value, ObjectMapper $mapper) : mixed + public function cast(mixed $value, ObjectMapper $mapper, ?string $expectedTypeName) : mixed { assert(is_array($value));