diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 87e9c71266ef7..38f9afb7ad442 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -543,6 +543,7 @@ private function writeIndex(&$array, $index, $value) * * @throws NoSuchPropertyException If the property does not exist or is not * public. + * @throws \TypeError */ private function writeProperty(&$object, $property, $value) { @@ -553,7 +554,7 @@ private function writeProperty(&$object, $property, $value) $access = $this->getWriteAccessInfo($object, $property, $value); if (self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]) { - $object->{$access[self::ACCESS_NAME]}($value); + $this->callMethod($object, $access[self::ACCESS_NAME], $value); } elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) { $object->{$access[self::ACCESS_NAME]} = $value; } elseif (self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE]) { @@ -567,12 +568,48 @@ private function writeProperty(&$object, $property, $value) $object->$property = $value; } elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) { - $object->{$access[self::ACCESS_NAME]}($value); + $this->callMethod($object, $access[self::ACCESS_NAME], $value); } else { throw new NoSuchPropertyException($access[self::ACCESS_NAME]); } } + /** + * Throws a {@see \TypeError} as in PHP 7 when using PHP 5. + * + * @param object $object + * @param string $method + * @param mixed $value + * + * @throws \TypeError + * @throws \Exception + */ + private function callMethod($object, $method, $value) { + if (PHP_MAJOR_VERSION >= 7) { + $object->{$method}($value); + + return; + } + + set_error_handler(function ($errno, $errstr) use ($object, $method) { + if (E_RECOVERABLE_ERROR === $errno && false !== strpos($errstr, sprintf('passed to %s::%s() must', get_class($object), $method))) { + throw new \TypeError($errstr); + } + + return false; + }); + + try { + $object->{$method}($value); + restore_error_handler(); + } catch (\Exception $e) { + // Cannot use finally in 5.5 because of https://bugs.php.net/bug.php?id=67047 + restore_error_handler(); + + throw $e; + } + } + /** * Adjusts a collection-valued property by calling add*() and remove*() * methods. @@ -582,6 +619,8 @@ private function writeProperty(&$object, $property, $value) * @param array|\Traversable $collection The collection to write * @param string $addMethod The add*() method * @param string $removeMethod The remove*() method + * + * @throws \TypeError */ private function writeCollection($object, $property, $collection, $addMethod, $removeMethod) { @@ -613,11 +652,11 @@ private function writeCollection($object, $property, $collection, $addMethod, $r } foreach ($itemToRemove as $item) { - $object->{$removeMethod}($item); + $this->callMethod($object, $removeMethod, $item); } foreach ($itemsToAdd as $item) { - $object->{$addMethod}($item); + $this->callMethod($object, $addMethod, $item); } } diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php b/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php index be41ee175d985..5def1f4555cd2 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php @@ -47,6 +47,8 @@ interface PropertyAccessorInterface * @throws Exception\AccessException If a property/index does not exist or is not public * @throws Exception\UnexpectedTypeException If a value within the path is neither object * nor array + * @throws \TypeError If a the type of the value does not match the type + * of the parameter of the mutator method */ public function setValue(&$objectOrArray, $propertyPath, $value); diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php index 7b1b927529afe..e63af3a8bac5d 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php @@ -26,6 +26,7 @@ class TestClass private $publicIsAccessor; private $publicHasAccessor; private $publicGetter; + private $date; public function __construct($value) { @@ -173,4 +174,14 @@ public function getPublicGetter() { return $this->publicGetter; } + + public function setDate(\DateTimeInterface $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 ce4438550e8d3..f2ab76d1cdff5 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -510,4 +510,21 @@ public function testIsWritableForReferenceChainIssue($object, $path, $value) { $this->assertEquals($value, $this->propertyAccessor->isWritable($object, $path)); } + + /** + * @expectedException \TypeError + */ + public function testThrowTypeError() + { + $this->propertyAccessor->setValue(new TestClass('Kévin'), 'date', 'This is a string, \DateTime excepted.'); + } + + public function testSetTypeHint() + { + $date = new \DateTimeImmutable(); + $object = new TestClass('Kévin'); + + $this->propertyAccessor->setValue($object, 'date', $date); + $this->assertSame($date, $object->getDate()); + } } diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index fc657c1d21f9f..2cecd9c133071 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.5.9" + "php": ">=5.5.9", + "symfony/polyfill-php70": "~1.0" }, "autoload": { "psr-4": { "Symfony\\Component\\PropertyAccess\\": "" },
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: