diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 3b9b49490da58..40ba789900721 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -400,6 +400,7 @@ private function writeIndex(&$array, $index, $value) * * @throws NoSuchPropertyException If the property does not exist or is not * public. + * @throws UnexpectedTypeException */ private function writeProperty(&$object, $property, $singular, $value) { @@ -410,7 +411,7 @@ private function writeProperty(&$object, $property, $singular, $value) $access = $this->getWriteAccessInfo($object, $property, $singular, $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]) { @@ -457,12 +458,78 @@ private function writeProperty(&$object, $property, $singular, $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 UnexpectedTypeException} as in PHP 7 when using PHP 5. + * + * @param object $object + * @param string $method + * @param mixed $value + * + * @throws UnexpectedTypeException + * @throws \Exception + */ + private function callMethod($object, $method, $value) { + if (PHP_MAJOR_VERSION >= 7) { + try { + $object->{$method}($value); + } catch (\TypeError $e) { + throw $this->createUnexpectedTypeException($object, $method, $value); + } + + return; + } + + $that = $this; + set_error_handler(function ($errno, $errstr) use ($object, $method, $value, $that) { + if (E_RECOVERABLE_ERROR === $errno && false !== strpos($errstr, sprintf('passed to %s::%s() must', get_class($object), $method))) { + throw $that->createUnexpectedTypeException($object, $method, $value); + } + + 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; + } + } + + /** + * Creates an UnexpectedTypeException. + * + * @param object $object + * @param string $method + * @param mixed $value + * + * @return UnexpectedTypeException + */ + private function createUnexpectedTypeException($object, $method, $value) + { + $reflectionMethod = new \ReflectionMethod($object, $method); + $parameters = $reflectionMethod->getParameters(); + + $expectedType = 'unknown'; + if (isset($parameters[0])) { + $class = $parameters[0]->getClass(); + if (null !== $class) { + $expectedType = $class->getName(); + } + } + + return new UnexpectedTypeException($value, $expectedType); + } + /** * Guesses how to write the property value. * diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php b/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php index ecedabc134ef4..755f5ccb3d683 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php @@ -45,7 +45,6 @@ interface PropertyAccessorInterface * * @throws Exception\NoSuchPropertyException If a property does not exist or is not public. * @throws Exception\UnexpectedTypeException If a value within the path is neither object - * nor array */ public function setValue(&$objectOrArray, $propertyPath, $value); diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php new file mode 100644 index 0000000000000..ca4c5745ae06c --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Tests\Fixtures; + +/** + * @author Kévin Dunglas + */ +class TypeHinted +{ + private $date; + + public function setDate(\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 51bc6eabc2af1..85ea84802790d 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -16,6 +16,7 @@ use Symfony\Component\PropertyAccess\Tests\Fixtures\Magician; use Symfony\Component\PropertyAccess\Tests\Fixtures\MagicianCall; use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object; +use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted; class PropertyAccessorTest extends \PHPUnit_Framework_TestCase { @@ -403,4 +404,22 @@ public function getValidPropertyPaths() array(array('root' => array('index' => array())), '[root][index][firstName]', null), ); } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage Expected argument of type "DateTime", "string" given + */ + public function testThrowTypeError() + { + $this->propertyAccessor->setValue(new TypeHinted(), 'date', 'This is a string, \DateTime excepted.'); + } + + public function testSetTypeHint() + { + $date = new \DateTime(); + $object = new TypeHinted(); + + $this->propertyAccessor->setValue($object, 'date', $date); + $this->assertSame($date, $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