From 3156a60760d27e36a840604d1733c844c9614618 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 10 May 2023 17:39:00 +0200 Subject: [PATCH] [PropertyAccess] Auto-cast from/to DateTime/Immutable when appropriate --- .../PropertyAccess/PropertyAccessor.php | 43 ++++++++++++------- .../Tests/Fixtures/TypeHinted.php | 5 +++ .../Tests/PropertyAccessorTest.php | 18 ++++++++ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 0bf5c0afa903a..ba676b9de3269 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -514,7 +514,7 @@ private function writeIndex(array $zval, string|int $index, mixed $value): void * * @throws NoSuchPropertyException if the property does not exist or is not public */ - private function writeProperty(array $zval, string $property, mixed $value): void + private function writeProperty(array $zval, string $property, mixed $value, bool $recursive = false): void { if (!\is_object($zval[self::VALUE])) { throw new NoSuchPropertyException(sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%1$s]" instead?', $property)); @@ -524,24 +524,37 @@ private function writeProperty(array $zval, string $property, mixed $value): voi $class = $object::class; $mutator = $this->getWriteInfo($class, $property, $value); - if (PropertyWriteInfo::TYPE_NONE !== $mutator->getType()) { - $type = $mutator->getType(); + try { + if (PropertyWriteInfo::TYPE_NONE !== $mutator->getType()) { + $type = $mutator->getType(); + + if (PropertyWriteInfo::TYPE_METHOD === $type) { + $object->{$mutator->getName()}($value); + } elseif (PropertyWriteInfo::TYPE_PROPERTY === $type) { + $object->{$mutator->getName()} = $value; + } elseif (PropertyWriteInfo::TYPE_ADDER_AND_REMOVER === $type) { + $this->writeCollection($zval, $property, $value, $mutator->getAdderInfo(), $mutator->getRemoverInfo()); + } + } elseif ($object instanceof \stdClass && property_exists($object, $property)) { + $object->$property = $value; + } elseif (!$this->ignoreInvalidProperty) { + if ($mutator->hasErrors()) { + throw new NoSuchPropertyException(implode('. ', $mutator->getErrors()).'.'); + } - if (PropertyWriteInfo::TYPE_METHOD === $type) { - $object->{$mutator->getName()}($value); - } elseif (PropertyWriteInfo::TYPE_PROPERTY === $type) { - $object->{$mutator->getName()} = $value; - } elseif (PropertyWriteInfo::TYPE_ADDER_AND_REMOVER === $type) { - $this->writeCollection($zval, $property, $value, $mutator->getAdderInfo(), $mutator->getRemoverInfo()); + throw new NoSuchPropertyException(sprintf('Could not determine access type for property "%s" in class "%s".', $property, get_debug_type($object))); } - } elseif ($object instanceof \stdClass && property_exists($object, $property)) { - $object->$property = $value; - } elseif (!$this->ignoreInvalidProperty) { - if ($mutator->hasErrors()) { - throw new NoSuchPropertyException(implode('. ', $mutator->getErrors()).'.'); + } catch (\TypeError $e) { + if ($recursive || !$value instanceof \DateTimeInterface || !\in_array($value::class, ['DateTime', 'DateTimeImmutable'], true) || __FILE__ !== $e->getTrace()[0]['file']) { + throw $e; } - throw new NoSuchPropertyException(sprintf('Could not determine access type for property "%s" in class "%s".', $property, get_debug_type($object))); + $value = $value instanceof \DateTimeImmutable ? \DateTime::createFromImmutable($value) : \DateTimeImmutable::createFromMutable($value); + try { + $this->writeProperty($zval, $property, $value, true); + } catch (\TypeError) { + throw $e; // throw the previous error + } } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php index bb0a26dbc5091..42b87df1ae39b 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php @@ -28,6 +28,11 @@ public function setDate(\DateTimeImmutable $date) $this->date = $date; } + public function setDateMutable(\DateTime $date) + { + $this->date = $date; + } + public function getDate() { return $this->date; diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index ae773e91d87ec..7b4dfba0759f5 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -940,4 +940,22 @@ public function testSetValueWrongTypeShouldThrowWrappedException() $this->expectExceptionMessage('Expected argument of type "float", "string" given at property path "publicProperty"'); $this->propertyAccessor->setValue($object, 'publicProperty', 'string'); } + + public function testCastDateTime() + { + $object = new TypeHinted(); + + $this->propertyAccessor->setValue($object, 'date', new \DateTime()); + + $this->assertInstanceOf(\DateTimeImmutable::class, $object->getDate()); + } + + public function testCastDateTimeImmutable() + { + $object = new TypeHinted(); + + $this->propertyAccessor->setValue($object, 'date_mutable', new \DateTimeImmutable()); + + $this->assertInstanceOf(\DateTime::class, $object->getDate()); + } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy