[PropertyAccess] Added isReadable() and isWritable()
This commit is contained in:
parent
20e6bf8f49
commit
6d2af217aa
@ -45,6 +45,47 @@ Form
|
|||||||
{
|
{
|
||||||
```
|
```
|
||||||
|
|
||||||
|
PropertyAccess
|
||||||
|
--------------
|
||||||
|
|
||||||
|
* The methods `isReadable()` and `isWritable()` were added to
|
||||||
|
`PropertyAccessorInterface`. If you implemented this interface in your own
|
||||||
|
code, you should add these two methods.
|
||||||
|
|
||||||
|
* The methods `getValue()` and `setValue()` now throw an
|
||||||
|
`NoSuchIndexException` instead of a `NoSuchPropertyException` when an index
|
||||||
|
is accessed on an object that does not implement `ArrayAccess`. If you catch
|
||||||
|
this exception in your code, you should adapt the catch statement:
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$object = new \stdClass();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$propertyAccessor->getValue($object, '[index]');
|
||||||
|
$propertyAccessor->setValue($object, '[index]', 'New value');
|
||||||
|
} catch (NoSuchPropertyException $e) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$object = new \stdClass();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$propertyAccessor->getValue($object, '[index]');
|
||||||
|
$propertyAccessor->setValue($object, '[index]', 'New value');
|
||||||
|
} catch (NoSuchIndexException $e) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A `NoSuchPropertyException` is still thrown when a non-existing property is
|
||||||
|
accessed on an object or an array.
|
||||||
|
|
||||||
Validator
|
Validator
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ CHANGELOG
|
|||||||
* [BC BREAK] when accessing an index on an object that does not implement
|
* [BC BREAK] when accessing an index on an object that does not implement
|
||||||
ArrayAccess, a NoSuchIndexException is now thrown instead of the
|
ArrayAccess, a NoSuchIndexException is now thrown instead of the
|
||||||
semantically wrong NoSuchPropertyException
|
semantically wrong NoSuchPropertyException
|
||||||
|
* [BC BREAK] added isReadable() and isWritable() to PropertyAccessorInterface
|
||||||
|
|
||||||
2.3.0
|
2.3.0
|
||||||
------
|
------
|
||||||
|
@ -105,6 +105,84 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isReadable($objectOrArray, $propertyPath)
|
||||||
|
{
|
||||||
|
if (is_string($propertyPath)) {
|
||||||
|
$propertyPath = new PropertyPath($propertyPath);
|
||||||
|
} elseif (!$propertyPath instanceof PropertyPathInterface) {
|
||||||
|
throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPathInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->readPropertiesUntil($objectOrArray, $propertyPath, $propertyPath->getLength(), $this->throwExceptionOnInvalidIndex);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (NoSuchIndexException $e) {
|
||||||
|
return false;
|
||||||
|
} catch (NoSuchPropertyException $e) {
|
||||||
|
return false;
|
||||||
|
} catch (UnexpectedTypeException $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isWritable($objectOrArray, $propertyPath, $value)
|
||||||
|
{
|
||||||
|
if (is_string($propertyPath)) {
|
||||||
|
$propertyPath = new PropertyPath($propertyPath);
|
||||||
|
} elseif (!$propertyPath instanceof PropertyPathInterface) {
|
||||||
|
throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPathInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$propertyValues = $this->readPropertiesUntil($objectOrArray, $propertyPath, $propertyPath->getLength() - 1);
|
||||||
|
$overwrite = true;
|
||||||
|
|
||||||
|
// Add the root object to the list
|
||||||
|
array_unshift($propertyValues, array(
|
||||||
|
self::VALUE => $objectOrArray,
|
||||||
|
self::IS_REF => true,
|
||||||
|
));
|
||||||
|
|
||||||
|
for ($i = count($propertyValues) - 1; $i >= 0; --$i) {
|
||||||
|
$objectOrArray = $propertyValues[$i][self::VALUE];
|
||||||
|
|
||||||
|
if ($overwrite) {
|
||||||
|
if (!is_object($objectOrArray) && !is_array($objectOrArray)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$property = $propertyPath->getElement($i);
|
||||||
|
|
||||||
|
if ($propertyPath->isIndex($i)) {
|
||||||
|
if (!$objectOrArray instanceof \ArrayAccess && !is_array($objectOrArray)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!$this->isPropertyWritable($objectOrArray, $property, $value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $objectOrArray;
|
||||||
|
$overwrite = !$propertyValues[$i][self::IS_REF];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (NoSuchIndexException $e) {
|
||||||
|
return false;
|
||||||
|
} catch (NoSuchPropertyException $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the path from an object up to a given path index.
|
* Reads the path from an object up to a given path index.
|
||||||
*
|
*
|
||||||
@ -357,9 +435,9 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
$setter = 'set'.$this->camelize($property);
|
$setter = 'set'.$this->camelize($property);
|
||||||
$classHasProperty = $reflClass->hasProperty($property);
|
$classHasProperty = $reflClass->hasProperty($property);
|
||||||
|
|
||||||
if ($reflClass->hasMethod($setter) && $reflClass->getMethod($setter)->isPublic()) {
|
if ($this->isMethodAccessible($reflClass, $setter, 1)) {
|
||||||
$object->$setter($value);
|
$object->$setter($value);
|
||||||
} elseif ($reflClass->hasMethod('__set') && $reflClass->getMethod('__set')->isPublic()) {
|
} elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
|
||||||
$object->$property = $value;
|
$object->$property = $value;
|
||||||
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
|
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
|
||||||
$object->$property = $value;
|
$object->$property = $value;
|
||||||
@ -370,7 +448,7 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
// returns true, consequently the following line will result in a
|
// returns true, consequently the following line will result in a
|
||||||
// fatal error.
|
// fatal error.
|
||||||
$object->$property = $value;
|
$object->$property = $value;
|
||||||
} elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) {
|
} elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) {
|
||||||
// we call the getter and hope the __call do the job
|
// we call the getter and hope the __call do the job
|
||||||
$object->$setter($value);
|
$object->$setter($value);
|
||||||
} else {
|
} else {
|
||||||
@ -385,6 +463,38 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function isPropertyWritable($object, $property, $value)
|
||||||
|
{
|
||||||
|
if (!is_object($object)) {
|
||||||
|
throw new NoSuchPropertyException(sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%s]" instead?', $property, $property));
|
||||||
|
}
|
||||||
|
|
||||||
|
$reflClass = new \ReflectionClass($object);
|
||||||
|
$plural = $this->camelize($property);
|
||||||
|
|
||||||
|
// Any of the two methods is required, but not yet known
|
||||||
|
$singulars = (array) StringUtil::singularify($plural);
|
||||||
|
|
||||||
|
if (is_array($value) || $value instanceof \Traversable) {
|
||||||
|
try {
|
||||||
|
if (null !== $this->findAdderAndRemover($reflClass, $singulars)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (NoSuchPropertyException $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$setter = 'set'.$this->camelize($property);
|
||||||
|
$classHasProperty = $reflClass->hasProperty($property);
|
||||||
|
|
||||||
|
return $this->isMethodAccessible($reflClass, $setter, 1)
|
||||||
|
|| $this->isMethodAccessible($reflClass, '__set', 2)
|
||||||
|
|| ($classHasProperty && $reflClass->getProperty($property)->isPublic())
|
||||||
|
|| (!$classHasProperty && property_exists($object, $property))
|
||||||
|
|| ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Camelizes a given string.
|
* Camelizes a given string.
|
||||||
*
|
*
|
||||||
@ -409,6 +519,8 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
*/
|
*/
|
||||||
private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars)
|
private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars)
|
||||||
{
|
{
|
||||||
|
$exception = null;
|
||||||
|
|
||||||
foreach ($singulars as $singular) {
|
foreach ($singulars as $singular) {
|
||||||
$addMethod = 'add'.$singular;
|
$addMethod = 'add'.$singular;
|
||||||
$removeMethod = 'remove'.$singular;
|
$removeMethod = 'remove'.$singular;
|
||||||
@ -420,8 +532,8 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
return array($addMethod, $removeMethod);
|
return array($addMethod, $removeMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($addMethodFound xor $removeMethodFound) {
|
if ($addMethodFound xor $removeMethodFound && null === $exception) {
|
||||||
throw new NoSuchPropertyException(sprintf(
|
$exception = new NoSuchPropertyException(sprintf(
|
||||||
'Found the public method "%s()", but did not find a public "%s()" on class %s',
|
'Found the public method "%s()", but did not find a public "%s()" on class %s',
|
||||||
$addMethodFound ? $addMethod : $removeMethod,
|
$addMethodFound ? $addMethod : $removeMethod,
|
||||||
$addMethodFound ? $removeMethod : $addMethod,
|
$addMethodFound ? $removeMethod : $addMethod,
|
||||||
@ -430,6 +542,10 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (null !== $exception) {
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ namespace Symfony\Component\PropertyAccess;
|
|||||||
interface PropertyAccessorInterface
|
interface PropertyAccessorInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Sets the value at the end of the property path of the object
|
* Sets the value at the end of the property path of the object graph.
|
||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
*
|
*
|
||||||
@ -50,7 +50,7 @@ interface PropertyAccessorInterface
|
|||||||
public function setValue(&$objectOrArray, $propertyPath, $value);
|
public function setValue(&$objectOrArray, $propertyPath, $value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value at the end of the property path of the object
|
* Returns the value at the end of the property path of the object graph.
|
||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
*
|
*
|
||||||
@ -78,4 +78,31 @@ interface PropertyAccessorInterface
|
|||||||
* @throws Exception\NoSuchPropertyException If a property does not exist or is not public.
|
* @throws Exception\NoSuchPropertyException If a property does not exist or is not public.
|
||||||
*/
|
*/
|
||||||
public function getValue($objectOrArray, $propertyPath);
|
public function getValue($objectOrArray, $propertyPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a value can be written at a given property path.
|
||||||
|
*
|
||||||
|
* Whenever this method returns true, {@link setValue()} is guaranteed not
|
||||||
|
* to throw an exception when called with the same arguments.
|
||||||
|
*
|
||||||
|
* @param object|array $objectOrArray The object or array to check
|
||||||
|
* @param string|PropertyPathInterface $propertyPath The property path to check
|
||||||
|
* @param mixed $value The value to set at the end of the property path
|
||||||
|
*
|
||||||
|
* @return Boolean Whether the value can be set
|
||||||
|
*/
|
||||||
|
public function isWritable($objectOrArray, $propertyPath, $value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a property path can be read from an object graph.
|
||||||
|
*
|
||||||
|
* Whenever this method returns true, {@link getValue()} is guaranteed not
|
||||||
|
* to throw an exception when called with the same arguments.
|
||||||
|
*
|
||||||
|
* @param object|array $objectOrArray The object or array to check
|
||||||
|
* @param string|PropertyPathInterface $propertyPath The property path to check
|
||||||
|
*
|
||||||
|
* @return Boolean Whether the property path can be read
|
||||||
|
*/
|
||||||
|
public function isReadable($objectOrArray, $propertyPath);
|
||||||
}
|
}
|
||||||
|
@ -210,11 +210,63 @@ abstract class PropertyAccessorCollectionTest extends \PHPUnit_Framework_TestCas
|
|||||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
|
* @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
|
* @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 testSetValueFailsIfNoAdderAndNoRemoverFound()
|
public function testSetValueFailsIfNoAdderNorRemoverFound()
|
||||||
{
|
{
|
||||||
$car = $this->getMock(__CLASS__.'_CarNoAdderAndRemover');
|
$car = $this->getMock(__CLASS__.'_CarNoAdderAndRemover');
|
||||||
$axes = $this->getCollection(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
$axes = $this->getCollection(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||||
|
|
||||||
$this->propertyAccessor->setValue($car, 'axes', $axes);
|
$this->propertyAccessor->setValue($car, 'axes', $axes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getValidPropertyPaths
|
||||||
|
*/
|
||||||
|
public function testIsReadable(array $array, $path)
|
||||||
|
{
|
||||||
|
$collection = $this->getCollection($array);
|
||||||
|
|
||||||
|
$this->assertTrue($this->propertyAccessor->isReadable($collection, $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getValidPropertyPaths
|
||||||
|
*/
|
||||||
|
public function testIsWritable(array $array, $path)
|
||||||
|
{
|
||||||
|
$collection = $this->getCollection($array);
|
||||||
|
|
||||||
|
$this->assertTrue($this->propertyAccessor->isWritable($collection, $path, 'Updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsWritableReturnsTrueIfAdderAndRemoverExists()
|
||||||
|
{
|
||||||
|
$car = $this->getMock(__CLASS__.'_Car');
|
||||||
|
$axes = $this->getCollection(array(1 => 'first', 2 => 'second', 3 => 'third'));
|
||||||
|
|
||||||
|
$this->assertTrue($this->propertyAccessor->isWritable($car, 'axes', $axes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsWritableReturnsFalseIfOnlyAdderExists()
|
||||||
|
{
|
||||||
|
$car = $this->getMock(__CLASS__.'_CarOnlyAdder');
|
||||||
|
$axes = $this->getCollection(array(1 => 'first', 2 => 'second', 3 => 'third'));
|
||||||
|
|
||||||
|
$this->assertFalse($this->propertyAccessor->isWritable($car, 'axes', $axes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsWritableReturnsFalseIfOnlyRemoverExists()
|
||||||
|
{
|
||||||
|
$car = $this->getMock(__CLASS__.'_CarOnlyRemover');
|
||||||
|
$axes = $this->getCollection(array(1 => 'first', 2 => 'second', 3 => 'third'));
|
||||||
|
|
||||||
|
$this->assertFalse($this->propertyAccessor->isWritable($car, 'axes', $axes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsWritableReturnsFalseIfNoAdderNorRemoverExists()
|
||||||
|
{
|
||||||
|
$car = $this->getMock(__CLASS__.'_CarNoAdderAndRemover');
|
||||||
|
$axes = $this->getCollection(array(1 => 'first', 2 => 'second', 3 => 'third'));
|
||||||
|
|
||||||
|
$this->assertFalse($this->propertyAccessor->isWritable($car, 'axes', $axes));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,4 +301,140 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$this->propertyAccessor->setValue($value, 'foobar', 'bam');
|
$this->propertyAccessor->setValue($value, 'foobar', 'bam');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getValidPropertyPaths
|
||||||
|
*/
|
||||||
|
public function testIsReadable($objectOrArray, $path)
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->propertyAccessor->isReadable($objectOrArray, $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getPathsWithMissingProperty
|
||||||
|
*/
|
||||||
|
public function testIsReadableReturnsFalseIfPropertyNotFound($objectOrArray, $path)
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getPathsWithMissingIndex
|
||||||
|
*/
|
||||||
|
public function testIsReadableReturnsTrueIfIndexNotFound($objectOrArray, $path)
|
||||||
|
{
|
||||||
|
// Non-existing indices can be read. In this case, null is returned
|
||||||
|
$this->assertTrue($this->propertyAccessor->isReadable($objectOrArray, $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getPathsWithMissingIndex
|
||||||
|
*/
|
||||||
|
public function testIsReadableReturnsFalseIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
|
||||||
|
{
|
||||||
|
$this->propertyAccessor = new PropertyAccessor(false, true);
|
||||||
|
|
||||||
|
// When exceptions are enabled, non-existing indices cannot be read
|
||||||
|
$this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsReadableRecognizesMagicGet()
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->propertyAccessor->isReadable(new TestClassMagicGet('Bernhard'), 'magicProperty'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsReadableDoesNotRecognizeMagicCallByDefault()
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->propertyAccessor->isReadable(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsReadableRecognizesMagicCallIfEnabled()
|
||||||
|
{
|
||||||
|
$this->propertyAccessor = new PropertyAccessor(true);
|
||||||
|
|
||||||
|
$this->assertTrue($this->propertyAccessor->isReadable(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsReadableThrowsExceptionIfNotObjectOrArray()
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->propertyAccessor->isReadable('baz', 'foobar'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsReadableThrowsExceptionIfNull()
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->propertyAccessor->isReadable(null, 'foobar'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsReadableThrowsExceptionIfEmpty()
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->propertyAccessor->isReadable('', 'foobar'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getValidPropertyPaths
|
||||||
|
*/
|
||||||
|
public function testIsWritable($objectOrArray, $path)
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path, 'Updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getPathsWithMissingProperty
|
||||||
|
*/
|
||||||
|
public function testIsWritableReturnsFalseIfPropertyNotFound($objectOrArray, $path)
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->propertyAccessor->isWritable($objectOrArray, $path, 'Updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getPathsWithMissingIndex
|
||||||
|
*/
|
||||||
|
public function testIsWritableReturnsTrueIfIndexNotFound($objectOrArray, $path)
|
||||||
|
{
|
||||||
|
// Non-existing indices can be written. Arrays are created on-demand.
|
||||||
|
$this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path, 'Updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getPathsWithMissingIndex
|
||||||
|
*/
|
||||||
|
public function testIsWritableReturnsTrueIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
|
||||||
|
{
|
||||||
|
$this->propertyAccessor = new PropertyAccessor(false, true);
|
||||||
|
|
||||||
|
// Non-existing indices can be written even if exceptions are enabled
|
||||||
|
$this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path, 'Updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsWritableRecognizesMagicSet()
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->propertyAccessor->isWritable(new TestClassMagicGet('Bernhard'), 'magicProperty', 'Updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsWritableDoesNotRecognizeMagicCallByDefault()
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->propertyAccessor->isWritable(new TestClassMagicCall('Bernhard'), 'magicCallProperty', 'Updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsWritableRecognizesMagicCallIfEnabled()
|
||||||
|
{
|
||||||
|
$this->propertyAccessor = new PropertyAccessor(true);
|
||||||
|
|
||||||
|
$this->assertTrue($this->propertyAccessor->isWritable(new TestClassMagicCall('Bernhard'), 'magicCallProperty', 'Updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsWritableThrowsExceptionIfNotObjectOrArray()
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->propertyAccessor->isWritable('baz', 'foobar', 'Updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsWritableThrowsExceptionIfNull()
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->propertyAccessor->isWritable(null, 'foobar', 'Updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsWritableThrowsExceptionIfEmpty()
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->propertyAccessor->isWritable('', 'foobar', 'Updated'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user