From 21e0e0798913a73804be7d84555e0cf6212f4385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 9 Feb 2016 17:23:20 +0100 Subject: [PATCH 1/2] [PropertyAccess] Throw an UnexpectedTypeException when the type do not match --- .../PropertyAccess/PropertyAccessor.php | 70 ++++++++++++++++++- .../PropertyAccessorInterface.php | 1 - .../Tests/Fixtures/TypeHinted.php | 30 ++++++++ .../Tests/PropertyAccessorTest.php | 19 +++++ 4 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Fixtures/TypeHinted.php diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 3b9b49490da58..7c37c75b54700 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,77 @@ 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; + } + + set_error_handler(function ($errno, $errstr) use ($object, $method, $value) { + if (E_RECOVERABLE_ERROR === $errno && false !== strpos($errstr, sprintf('passed to %s::%s() must', get_class($object), $method))) { + throw $this->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()); + } } From 44037d951cd0102f613d029821aa0ec71853618d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 6 Mar 2016 19:34:59 +0100 Subject: [PATCH 2/2] Fix Using when not in object context --- src/Symfony/Component/PropertyAccess/PropertyAccessor.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 7c37c75b54700..40ba789900721 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -485,9 +485,10 @@ private function callMethod($object, $method, $value) { return; } - set_error_handler(function ($errno, $errstr) use ($object, $method, $value) { + $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 $this->createUnexpectedTypeException($object, $method, $value); + throw $that->createUnexpectedTypeException($object, $method, $value); } return false; 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