diff --git a/src/Symfony/Component/PropertyAccess/CHANGELOG.md b/src/Symfony/Component/PropertyAccess/CHANGELOG.md index fb5ebfa550..bfe3d51459 100644 --- a/src/Symfony/Component/PropertyAccess/CHANGELOG.md +++ b/src/Symfony/Component/PropertyAccess/CHANGELOG.md @@ -5,6 +5,9 @@ CHANGELOG ------ * allowed non alpha numeric characters in second level and deeper object properties names + * [BC BREAK] when accessing an index on an object that does not implement + ArrayAccess, a NoSuchIndexException is now thrown instead of the + semantically wrong NoSuchPropertyException 2.3.0 ------ diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index f5183a109e..66671c5be0 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -160,12 +160,12 @@ class PropertyAccessor implements PropertyAccessorInterface * * @return mixed The value of the key * - * @throws NoSuchPropertyException If the array does not implement \ArrayAccess or it is not an array + * @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array */ private function &readIndex(&$array, $index) { if (!$array instanceof \ArrayAccess && !is_array($array)) { - throw new NoSuchPropertyException(sprintf('Index "%s" cannot be read from object of type "%s" because it doesn\'t implement \ArrayAccess', $index, get_class($array))); + throw new NoSuchIndexException(sprintf('Index "%s" cannot be read from object of type "%s" because it doesn\'t implement \ArrayAccess', $index, get_class($array))); } // Use an array instead of an object since performance is very crucial here @@ -271,12 +271,12 @@ class PropertyAccessor implements PropertyAccessorInterface * @param string|integer $index The index to write at * @param mixed $value The value to write * - * @throws NoSuchPropertyException If the array does not implement \ArrayAccess or it is not an array + * @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array */ private function writeIndex(&$array, $index, $value) { if (!$array instanceof \ArrayAccess && !is_array($array)) { - throw new NoSuchPropertyException(sprintf('Index "%s" cannot be modified in object of type "%s" because it doesn\'t implement \ArrayAccess', $index, get_class($array))); + throw new NoSuchIndexException(sprintf('Index "%s" cannot be modified in object of type "%s" because it doesn\'t implement \ArrayAccess', $index, get_class($array))); } $array[$index] = $value; diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php index b0f75aa366..808c9e1449 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php @@ -47,19 +47,6 @@ class PropertyAccessorCollectionTest_Car } } -class PropertyAccessorCollectionTest_CarCustomSingular -{ - public function addFoo($axis) {} - - public function removeFoo($axis) {} - - public function getAxes() {} -} - -class PropertyAccessorCollectionTest_Engine -{ -} - class PropertyAccessorCollectionTest_CarOnlyAdder { public function addAxis($axis) {} @@ -79,13 +66,6 @@ class PropertyAccessorCollectionTest_CarNoAdderAndRemover public function getAxes() {} } -class PropertyAccessorCollectionTest_CarNoAdderAndRemoverWithProperty -{ - protected $axes = array(); - - public function getAxes() {} -} - class PropertyAccessorCollectionTest_CompositeCar { public function getStructure() {} @@ -116,52 +96,34 @@ abstract class PropertyAccessorCollectionTest extends \PHPUnit_Framework_TestCas abstract protected function getCollection(array $array); - public function testGetValueReadsArrayAccess() + public function getValidPropertyPaths() { - $object = $this->getCollection(array('firstName' => 'Bernhard')); - - $this->assertEquals('Bernhard', $this->propertyAccessor->getValue($object, '[firstName]')); - } - - public function testGetValueReadsNestedArrayAccess() - { - $object = $this->getCollection(array('person' => array('firstName' => 'Bernhard'))); - - $this->assertEquals('Bernhard', $this->propertyAccessor->getValue($object, '[person][firstName]')); + return array( + array(array('firstName' => 'Bernhard'), '[firstName]', 'Bernhard'), + array(array('person' => array('firstName' => 'Bernhard')), '[person][firstName]', 'Bernhard'), + ); } /** - * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + * @dataProvider getValidPropertyPaths */ - public function testGetValueThrowsExceptionIfArrayAccessExpected() + public function testGetValue(array $array, $path, $value) { - $this->propertyAccessor->getValue(new \stdClass(), '[firstName]'); - } + $collection = $this->getCollection($array); - public function testSetValueUpdatesArrayAccess() - { - $object = $this->getCollection(array()); - - $this->propertyAccessor->setValue($object, '[firstName]', 'Bernhard'); - - $this->assertEquals('Bernhard', $object['firstName']); - } - - public function testSetValueUpdatesNestedArrayAccess() - { - $object = $this->getCollection(array()); - - $this->propertyAccessor->setValue($object, '[person][firstName]', 'Bernhard'); - - $this->assertEquals('Bernhard', $object['person']['firstName']); + $this->assertSame($value, $this->propertyAccessor->getValue($collection, $path)); } /** - * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + * @dataProvider getValidPropertyPaths */ - public function testSetValueThrowsExceptionIfArrayAccessExpected() + public function testSetValue(array $array, $path) { - $this->propertyAccessor->setValue(new \stdClass(), '[firstName]', 'Bernhard'); + $collection = $this->getCollection($array); + + $this->propertyAccessor->setValue($collection, $path, 'Updated'); + + $this->assertSame('Updated', $this->propertyAccessor->getValue($collection, $path)); } public function testSetValueCallsAdderAndRemoverForCollections() @@ -210,32 +172,9 @@ abstract class PropertyAccessorCollectionTest extends \PHPUnit_Framework_TestCas $this->propertyAccessor->setValue($car, 'structure.axes', $axesAfter); } - public function testSetValueCallsCustomAdderAndRemover() - { - $this->markTestSkipped('This feature is temporarily disabled as of 2.1'); - - $car = $this->getMock(__CLASS__.'_CarCustomSingular'); - $axesBefore = $this->getCollection(array(1 => 'second', 3 => 'fourth')); - $axesAfter = $this->getCollection(array(0 => 'first', 1 => 'second', 2 => 'third')); - - $car->expects($this->at(0)) - ->method('getAxes') - ->will($this->returnValue($axesBefore)); - $car->expects($this->at(1)) - ->method('removeFoo') - ->with('fourth'); - $car->expects($this->at(2)) - ->method('addFoo') - ->with('first'); - $car->expects($this->at(3)) - ->method('addFoo') - ->with('third'); - - $this->propertyAccessor->setValue($car, 'axes|foo', $axesAfter); - } - /** * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + * @expectedExceptionMessage Found the public method "addAxis()", but did not find a public "removeAxis()" on class Mock_PropertyAccessorCollectionTest_CarOnlyAdder */ public function testSetValueFailsIfOnlyAdderFound() { @@ -252,6 +191,7 @@ abstract class PropertyAccessorCollectionTest extends \PHPUnit_Framework_TestCas /** * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + * @expectedExceptionMessage Found the public method "removeAxis()", but did not find a public "addAxis()" on class Mock_PropertyAccessorCollectionTest_CarOnlyRemover */ public function testSetValueFailsIfOnlyRemoverFound() { @@ -267,58 +207,14 @@ abstract class PropertyAccessorCollectionTest extends \PHPUnit_Framework_TestCas } /** - * @dataProvider noAdderRemoverData + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + * @expectedExceptionMessage Neither the property "axes" nor one of the methods "addAx()", "addAxe()", "addAxis()", "setAxes()", "__set()" or "__call()" exist and have public access in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover */ - public function testNoAdderAndRemoverThrowsSensibleError($car, $path, $message) + public function testSetValueFailsIfNoAdderAndNoRemoverFound() { + $car = $this->getMock(__CLASS__.'_CarNoAdderAndRemover'); $axes = $this->getCollection(array(0 => 'first', 1 => 'second', 2 => 'third')); - try { - $this->propertyAccessor->setValue($car, $path, $axes); - $this->fail('An expected exception was not thrown!'); - } catch (ExceptionInterface $e) { - $this->assertEquals($message, $e->getMessage()); - } - } - - public function noAdderRemoverData() - { - $data = array(); - - $car = $this->getMock(__CLASS__.'_CarNoAdderAndRemover'); - $propertyPath = 'axes'; - $expectedMessage = sprintf( - 'Neither the property "axes" nor one of the methods "addAx()", '. - '"addAxe()", "addAxis()", "setAxes()", "__set()" or "__call()" exist and have '. - 'public access in class "%s".', - get_class($car) - ); - $data[] = array($car, $propertyPath, $expectedMessage); - - /* - Temporarily disabled in 2.1 - - $propertyPath = new PropertyPath('axes|boo'); - $expectedMessage = sprintf( - 'Neither element "axes" nor method "setAxes()" exists in class ' - .'"%s", nor could adders and removers be found based on the ' - .'passed singular: %s', - get_class($car), - 'boo' - ); - $data[] = array($car, $propertyPath, $expectedMessage); - */ - - $car = $this->getMock(__CLASS__.'_CarNoAdderAndRemoverWithProperty'); - $propertyPath = 'axes'; - $expectedMessage = sprintf( - 'Neither the property "axes" nor one of the methods "addAx()", '. - '"addAxe()", "addAxis()", "setAxes()", "__set()" or "__call()" exist and have '. - 'public access in class "%s".', - get_class($car) - ); - $data[] = array($car, $propertyPath, $expectedMessage); - - return $data; + $this->propertyAccessor->setValue($car, 'axes', $axes); } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index 3c13e50bb2..2d8b97dc57 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -31,7 +31,6 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase public function getValidPropertyPaths() { - return array( array(array('Bernhard', 'Schussek'), '[0]', 'Bernhard'), array(array('Bernhard', 'Schussek'), '[1]', 'Schussek'), @@ -65,7 +64,6 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase public function getPathsWithMissingProperty() { - return array( array((object) array('firstName' => 'Bernhard'), 'lastName'), array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.lastName'), @@ -86,7 +84,6 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase public function getPathsWithMissingIndex() { - return array( array(array('firstName' => 'Bernhard'), '[lastName]'), array(array(), '[index][lastName]'), @@ -131,6 +128,14 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase $this->propertyAccessor->getValue($objectOrArray, $path); } + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException + */ + public function testGetValueThrowsExceptionIfNotArrayAccess() + { + $this->propertyAccessor->getValue(new \stdClass(), '[index]'); + } + public function testGetValueReadsMagicGet() { $this->assertSame('Bernhard', $this->propertyAccessor->getValue(new TestClassMagicGet('Bernhard'), 'magicProperty')); @@ -229,6 +234,14 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase $this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path)); } + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException + */ + public function testSetValueThrowsExceptionIfNotArrayAccess() + { + $this->propertyAccessor->setValue(new \stdClass(), '[index]', 'Updated'); + } + public function testSetValueUpdatesMagicSet() { $author = new TestClassMagicGet('Bernhard');