8000 feature #30545 #30536 PropertyAccessor->getValue disable exception (d… · symfony/symfony@4e2b655 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4e2b655

Browse files
committed
feature #30545 #30536 PropertyAccessor->getValue disable exception (dimabory)
This PR was merged into the 4.3-dev branch. Discussion ---------- #30536 PropertyAccessor->getValue disable exception | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #30536 | License | MIT | Doc PR | symfony/symfony-docs#11156 Commits ------- c336696 [PropertyAccess] Allow to disable exception on invalid property path when using PropertyAccess::getValue()
2 parents e6eb43b + c336696 commit 4e2b655

File tree

14 files changed

+98
-10
lines changed

14 files changed

+98
-10
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ CHANGELOG
3030
* Added support for boolean container parameters within routes.
3131
* Added the `messenger:setup-transports` command to setup messenger transports
3232
* Added a `InMemoryTransport` to Messenger. Use it with a DSN starting with `in-memory://`.
33+
* Added `framework.property_access.throw_exception_on_invalid_property_path` config option.
3334

3435
4.2.0
3536
-----

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,7 @@ private function addPropertyAccessSection(ArrayNodeDefinition $rootNode)
903903
->children()
904904
->booleanNode('magic_call')->defaultFalse()->end()
905905
->booleanNode('throw_exception_on_invalid_index')->defaultFalse()->end()
906+
->booleanNode('throw_exception_on_invalid_property_path')->defaultTrue()->end()
906907
->end()
907908
->end()
908909
->end()

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui
13611361
->getDefinition('property_accessor')
13621362
->replaceArgument(0, $config['magic_call'])
13631363
->replaceArgument(1, $config['throw_exception_on_invalid_index'])
1364+
->replaceArgument(3, $config['throw_exception_on_invalid_property_path'])
13641365
;
13651366
}
13661367

src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<argument /> <!-- magicCall, set by the extension -->
1212
<argument /> <!-- throwExceptionOnInvalidIndex, set by the extension -->
1313
<argument type="service" id="cache.property_access" on-invalid="ignore" />
14+
<argument /> <!-- throwExceptionOnInvalidPropertyPath, set by the extension -->
1415
</service>
1516
<service id="Symfony\Component\PropertyAccess\PropertyAccessorInterface" alias="property_accessor" />
1617
</services>

src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@
233233
<xsd:complexType name="property_access">
234234
<xsd:attribute name="magic-call" type="xsd:boolean" />
235235
<xsd:attribute name="throw-exception-on-invalid-index" type="xsd:boolean" />
236+
<xsd:attribute name="throw-exception-on-invalid-property-path" type="xsd:boolean" />
236237
</xsd:complexType>
237238

238239
<xsd:complexType name="serializer">

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ protected static function getBundleDefaultConfig()
249249
'property_access' => [
250250
'magic_call' => false,
251251
'throw_exception_on_invalid_index' => false,
252+
'throw_exception_on_invalid_property_path' => true,
252253
],
253254
'property_info' => [
254255
'enabled' => !class_exists(FullStack::class),

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
'property_access' => [
55
'magic_call' => true,
66
'throw_exception_on_invalid_index' => true,
7+
'throw_exception_on_invalid_property_path' => false,
78
],
89
]);

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
88

99
<framework:config>
10-
<framework:property-access magic-call="true" throw-exception-on-invalid-index="true" />
10+
<framework:property-access magic-call="true" throw-exception-on-invalid-index="true" throw-exception-on-invalid-property-path="false"/>
1111
</framework:config>
1212
</container>

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ framework:
22
property_access:
33
magic_call: true
44
throw_exception_on_invalid_index: true
5+
throw_exception_on_invalid_property_path: false

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public function testPropertyAccessWithDefaultValue()
8080
$def = $container->getDefinition('property_accessor');
8181
$this->assertFalse($def->getArgument(0));
8282
$this->assertFalse($def->getArgument(1));
83+
$this->assertTrue($def->getArgument(3));
8384
}
8485

8586
public function testPropertyAccessWithOverriddenValues()
@@ -88,6 +89,7 @@ public function testPropertyAccessWithOverriddenValues()
8889
$def = $container->getDefinition('property_accessor');
8990
$this->assertTrue($def->getArgument(0));
9091
$this->assertTrue($def->getArgument(1));
92+
$this->assertFalse($def->getArgument(3));
9193
}
9294

9395
public function testPropertyAccessCache()

src/Symfony/Component/PropertyAccess/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
CHANGELOG
22
=========
33

4+
4.3.0
5+
-----
6+
7+
* added a `$throwExceptionOnInvalidPropertyPath` argument to the PropertyAccessor constructor.
8+
* added `enableExceptionOnInvalidPropertyPath()`, `disableExceptionOnInvalidPropertyPath()` and
9+
`isExceptionOnInvalidPropertyPath()` methods to `PropertyAccessorBuilder`
10+
411
4.0.0
512
-----
613

src/Symfony/Component/PropertyAccess/PropertyAccessor.php

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class PropertyAccessor implements PropertyAccessorInterface
5656
*/
5757
private $magicCall;
5858
private $ignoreInvalidIndices;
59+
private $ignoreInvalidProperty;
5960

6061
/**
6162
* @var CacheItemPoolInterface
@@ -70,11 +71,12 @@ class PropertyAccessor implements PropertyAccessorInterface
7071
* Should not be used by application code. Use
7172
* {@link PropertyAccess::createPropertyAccessor()} instead.
7273
*/
73-
public function __construct(bool $magicCall = false, bool $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null)
74+
public function __construct(bool $magicCall = false, bool $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null, bool $throwExceptionOnInvalidPropertyPath = true)
7475
{
7576
$this->magicCall = $magicCall;
7677
$this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex;
7778
$this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool; // Replace the NullAdapter by the null value
79+
$this->ignoreInvalidProperty = !$throwExceptionOnInvalidPropertyPath;
7880
}
7981

8082
/**
@@ -87,7 +89,7 @@ public function getValue($objectOrArray, $propertyPath)
8789
];
8890

8991
if (\is_object($objectOrArray) && false === strpbrk((string) $propertyPath, '.[')) {
90-
return $this->readProperty($zval, $propertyPath)[self::VALUE];
92+
return $this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty)[self::VALUE];
9193
}
9294

9395
$propertyPath = $this->getPropertyPath($propertyPath);
@@ -313,7 +315,7 @@ private function readPropertiesUntil($zval, PropertyPathInterface $propertyPath,
313315

314316
$zval = $this->readIndex($zval, $property);
315317
} else {
316-
$zval = $this->readProperty($zval, $property);
318+
$zval = $this->readProperty($zval, $property, $this->ignoreInvalidProperty);
317319
}
318320

319321
// the final value of the path must not be validated
@@ -372,14 +374,15 @@ private function readIndex($zval, $index)
372374
93C6 /**
373375
* Reads the a property from an object.
374376
*
375-
* @param array $zval The array containing the object to read from
376-
* @param string $property The property to read
377+
* @param array $zval The array containing the object to read from
378+
* @param string $property The property to read
379+
* @param bool $ignoreInvalidProperty Whether to ignore invalid property or throw an exception
377380
*
378381
* @return array The array containing the value of the property
379382
*
380-
* @throws NoSuchPropertyException if the property does not exist or is not public
383+
* @throws NoSuchPropertyException If $ignoreInvalidProperty is false and the property does not exist or is not public
381384
*/
382-
private function readProperty($zval, $property)
385+
private function readProperty($zval, $property, bool $ignoreInvalidProperty = false)
383386
{
384387
if (!\is_object($zval[self::VALUE])) {
385388
throw new NoSuchPropertyException(sprintf('Cannot read property "%s" from an array. Maybe you intended to write the property path as "[%1$s]" instead.', $property));
@@ -411,7 +414,7 @@ private function readProperty($zval, $property)
411414
} elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) {
412415
// we call the getter and hope the __call do the job
413416
$result[self::VALUE] = $object->{$access[self::ACCESS_NAME]}();
414-
} else {
417+
} elseif (!$ignoreInvalidProperty) {
415418
throw new NoSuchPropertyException($access[self::ACCESS_NAME]);
416419
}
417420

src/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class PropertyAccessorBuilder
2222
{
2323
private $magicCall = false;
2424
private $throwExceptionOnInvalidIndex = false;
25+
private $throwExceptionOnInvalidPropertyPath = true;
2526

2627
/**
2728
* @var CacheItemPoolInterface|null
@@ -97,6 +98,43 @@ public function isExceptionOnInvalidIndexEnabled()
9798
return $this->throwExceptionOnInvalidIndex;
9899
}
99100

101+
/**
102+
* Enables exceptions when reading a non-existing property.
103+
*
104+
* This has no influence on writing non-existing indices with PropertyAccessorInterface::setValue()
105+
* which are always created on-the-fly.
106+
*
107+
* @return $this
108+
*/
109+
public function enableExceptionOnInvalidPropertyPath()
110+
{
111+
$this->throwExceptionOnInvalidPropertyPath = true;
112+
113+
return $this;
114+
}
115+
116+
/**
117+
* Disables exceptions when reading a non-existing index.
118+
*
119+
* Instead, null is returned when calling PropertyAccessorInterface::getValue() on a non-existing index.
120+
*
121+
* @return $this
122+
*/
123+
public function disableExceptionOnInvalidPropertyPath()
124+
{
125+
$this->throwExceptionOnInvalidPropertyPath = false;
126+
127+
return $this;
128+
}
129+
130+
/**
131+
* @return bool whether an exception is thrown or null is returned when reading a non-existing property
132+
*/
133+
public function isExceptionOnInvalidPropertyPath()
134+
{
135+
return $this->throwExceptionOnInvalidPropertyPath;
136+
}
137+
100138
/**
101139
* Sets a cache system.
102140
*
@@ -128,6 +166,6 @@ public function getCacheItemPool()
128166
*/
129167
public function getPropertyAccessor()
130168
{
131-
return new PropertyAccessor($this->magicCall, $this->throwExceptionOnInvalidIndex, $this->cacheItemPool);
169+
return new PropertyAccessor($this->magicCall, $this->throwExceptionOnInvalidIndex, $this->cacheItemPool, $this->throwExceptionOnInvalidPropertyPath);
132170
}
133171
}

src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Cache\Adapter\ArrayAdapter;
1616
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
17+
use Symfony\Component\PropertyAccess\PropertyAccess;
1718
use Symfony\Component\PropertyAccess\PropertyAccessor;
1819
use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped;
1920
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClass;
@@ -100,6 +101,16 @@ public function testGetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $p
100101
$this->propertyAccessor->getValue($objectOrArray, $path);
101102
}
102103

104+
/**
105+
* @dataProvider getPathsWithMissingProperty
106+
*/
107+
public function testGetValueReturnsNullIfPropertyNotFoundAndExceptionIsDisabled($objectOrArray, $path)
108+
{
109+
$this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()->disableExceptionOnInvalidPropertyPath()->getPropertyAccessor();
110+
111+
$this->assertNull($this->propertyAccessor->getValue($objectOrArray, $path), $path);
112+
}
113+
103114
/**
104115
* @dataProvider getPathsWithMissingIndex
105116
*/
@@ -618,6 +629,25 @@ public function testAnonymousClassRead()
618629
$this->assertEquals($value, $propertyAccessor->getValue($obj, 'foo'));
619630
}
620631

632+
/**
633+
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
634+
*/
635+
public function testAnonymousClassReadThrowExceptionOnInvalidPropertyPath()
636+
{
637+
$obj = $this->generateAnonymousClass('bar');
638+
639+
$this->propertyAccessor->getValue($obj, 'invalid_property');
640+
}
641+
642+
public function testAnonymousClassReadReturnsNullOnInvalidPropertyWithDisabledException()
643+
{
644+
$obj = $this->generateAnonymousClass('bar');
645+
646+
$this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()->disableExceptionOnInvalidPropertyPath()->getPropertyAccessor();
647+
648+
$this->assertNull($this->propertyAccessor->getValue($obj, 'invalid_property'));
649+
}
650+
621651
public function testAnonymousClassWrite()
622652
{
623653
$value = 'bar';

0 commit comments

Comments
 (0)
0