From b286863bec86506cfe6b5ab65f698efb36bf5ddf Mon Sep 17 00:00:00 2001 From: Mikael Pajunen Date: Thu, 8 Jan 2015 19:24:48 +0200 Subject: [PATCH] [PropertyAccess] Show property path in all exception messages --- UPGRADE-2.7.md | 22 ++++++++++++++ .../Component/PropertyAccess/CHANGELOG.md | 6 ++++ .../Exception/UnexpectedTypeException.php | 29 +++++++++++++++++-- .../PropertyAccess/PropertyAccessor.php | 4 +-- .../Component/PropertyAccess/PropertyPath.php | 11 +++++-- .../Tests/PropertyAccessorTest.php | 26 +++++++++++++++++ .../PropertyAccess/Tests/PropertyPathTest.php | 4 +-- 7 files changed, 93 insertions(+), 9 deletions(-) diff --git a/UPGRADE-2.7.md b/UPGRADE-2.7.md index f209404d96..137773d647 100644 --- a/UPGRADE-2.7.md +++ b/UPGRADE-2.7.md @@ -83,3 +83,25 @@ Serializer $nameConverter = new CamelCaseToSnakeCaseNameConverter(array('fooBar', 'barFoo')); $normalizer = new GetSetMethodNormalizer(null, $nameConverter); ``` + +PropertyAccess +-------------- + + * `UnexpectedTypeException` now expects three constructor arguments: The invalid property value, + the `PropertyPathInterface` object and the current index of the property path. + + Before: + + ```php + use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; + + new UnexpectedTypeException($value, $expectedType); + ``` + + After: + + ```php + use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; + + new UnexpectedTypeException($value, $path, $pathIndex); + ``` diff --git a/src/Symfony/Component/PropertyAccess/CHANGELOG.md b/src/Symfony/Component/PropertyAccess/CHANGELOG.md index 631c9d7256..574106e521 100644 --- a/src/Symfony/Component/PropertyAccess/CHANGELOG.md +++ b/src/Symfony/Component/PropertyAccess/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +2.7.0 +------ + + * `UnexpectedTypeException` now expects three constructor arguments: The invalid property value, + the `PropertyPathInterface` object and the current index of the property path. + 2.5.0 ------ diff --git a/src/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php b/src/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php index 029d48c22a..5b87244f9d 100644 --- a/src/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php +++ b/src/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php @@ -11,6 +11,8 @@ namespace Symfony\Component\PropertyAccess\Exception; +use Symfony\Component\PropertyAccess\PropertyPathInterface; + /** * Thrown when a value does not match an expected type. * @@ -18,8 +20,31 @@ namespace Symfony\Component\PropertyAccess\Exception; */ class UnexpectedTypeException extends RuntimeException { - public function __construct($value, $expectedType) + /** + * @param mixed $value The unexpected value found while traversing property path + * @param PropertyPathInterface $path The property path + * @param int $pathIndex The property path index when the unexpected value was found + */ + public function __construct($value, $path, $pathIndex = null) { - parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); + if (func_num_args() === 3 && $path instanceof PropertyPathInterface) { + $message = sprintf( + 'PropertyAccessor requires a graph of objects or arrays to operate on, '. + 'but it found type "%s" while trying to traverse path "%s" at property "%s".', + gettype($value), + (string) $path, + $path->getElement($pathIndex) + ); + } else { + trigger_error('The '.__CLASS__.' constructor now expects 3 arguments: the invalid property value, the '.__NAMESPACE__.'\PropertyPathInterface object and the current index of the property path.', E_USER_DEPRECATED); + + $message = sprintf( + 'Expected argument of type "%s", "%s" given', + $path, + is_object($value) ? get_class($value) : gettype($value) + ); + } + + parent::__construct($message); } } diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 5514cb2ba6..74462fd3a2 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -97,7 +97,7 @@ class PropertyAccessor implements PropertyAccessorInterface if ($overwrite) { if (!is_object($objectOrArray) && !is_array($objectOrArray)) { - throw new UnexpectedTypeException($objectOrArray, 'object or array'); + throw new UnexpectedTypeException($objectOrArray, $propertyPath, $i); } $property = $propertyPath->getElement($i); @@ -221,7 +221,7 @@ class PropertyAccessor implements PropertyAccessorInterface for ($i = 0; $i < $lastIndex; ++$i) { if (!is_object($objectOrArray) && !is_array($objectOrArray)) { - throw new UnexpectedTypeException($objectOrArray, 'object or array'); + throw new UnexpectedTypeException($objectOrArray, $propertyPath, $i); } $property = $propertyPath->getElement($i); diff --git a/src/Symfony/Component/PropertyAccess/PropertyPath.php b/src/Symfony/Component/PropertyAccess/PropertyPath.php index 34cf1bbe60..ac3851dbbd 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyPath.php +++ b/src/Symfony/Component/PropertyAccess/PropertyPath.php @@ -11,9 +11,9 @@ namespace Symfony\Component\PropertyAccess; +use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException; use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException; -use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; /** * Default implementation of {@link PropertyPathInterface}. @@ -70,7 +70,7 @@ class PropertyPath implements \IteratorAggregate, PropertyPathInterface * * @param PropertyPath|string $propertyPath The property path as string or instance * - * @throws UnexpectedTypeException If the given path is not a string + * @throws InvalidArgumentException If the given path is not a string * @throws InvalidPropertyPathException If the syntax of the property path is not valid */ public function __construct($propertyPath) @@ -87,7 +87,12 @@ class PropertyPath implements \IteratorAggregate, PropertyPathInterface return; } if (!is_string($propertyPath)) { - throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPath'); + throw new InvalidArgumentException(sprintf( + 'The property path constructor needs a string or an instance of '. + '"Symfony\Component\PropertyAccess\PropertyPath". '. + 'Got: "%s"', + is_object($propertyPath) ? get_class($propertyPath) : gettype($propertyPath) + )); } if ('' === $propertyPath) { diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index 143ae7405d..2d29e3f5f4 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -139,6 +139,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "string" while trying to traverse path "foobar" at property "foobar". */ public function testGetValueThrowsExceptionIfNotObjectOrArray() { @@ -147,6 +148,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "NULL" while trying to traverse path "foobar" at property "foobar". */ public function testGetValueThrowsExceptionIfNull() { @@ -155,12 +157,22 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "string" while trying to traverse path "foobar" at property "foobar". */ public function testGetValueThrowsExceptionIfEmpty() { $this->propertyAccessor->getValue('', 'foobar'); } + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "NULL" while trying to traverse path "foobar.baz" at property "baz". + */ + public function testGetValueNestedExceptionMessage() + { + $this->propertyAccessor->getValue((object) array('foobar' => null), 'foobar.baz'); + } + /** * @dataProvider getValidPropertyPaths */ @@ -249,6 +261,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "string" while trying to traverse path "foobar" at property "foobar". */ public function testSetValueThrowsExceptionIfNotObjectOrArray() { @@ -259,6 +272,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "NULL" while trying to traverse path "foobar" at property "foobar". */ public function testSetValueThrowsExceptionIfNull() { @@ -269,6 +283,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "string" while trying to traverse path "foobar" at property "foobar". */ public function testSetValueThrowsExceptionIfEmpty() { @@ -277,6 +292,17 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase $this->propertyAccessor->setValue($value, 'foobar', 'bam'); } + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "NULL" while trying to traverse path "foobar.baz" at property "baz". + */ + public function testSetValueNestedExceptionMessage() + { + $value = (object) array('foobar' => null); + + $this->propertyAccessor->setValue($value, 'foobar.baz', 'bam'); + } + public function testGetValueWhenArrayValueIsNull() { $this->propertyAccessor = new PropertyAccessor(false, true); diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php index c6f1fd6e14..aa8c1288b0 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php @@ -69,7 +69,7 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase } /** - * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException */ public function testPathCannotBeNull() { @@ -77,7 +77,7 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase } /** - * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException */ public function testPathCannotBeFalse() {