diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 0bf5c0afa903..ba676b9de326 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 bb0a26dbc509..42b87df1ae39 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 ae773e91d87e..7b4dfba0759f 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()); + } }
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: