Skip to content

Commit ab25446

Browse files
committed
use bitwise flags to configure when the property accessor should throw
1 parent a760037 commit ab25446

File tree

10 files changed

+117
-28
lines changed

10 files changed

+117
-28
lines changed

UPGRADE-5.3.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ PhpunitBridge
4343

4444
* Deprecated the `SetUpTearDownTrait` trait, use original methods with "void" return typehint.
4545

46+
PropertyAccess
47+
--------------
48+
49+
* Deprecated passing a boolean as the second argument of `PropertyAccessor::__construct()`.
50+
Pass a combination of bitwise flags instead.
51+
4652
PropertyInfo
4753
------------
4854

UPGRADE-6.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ PhpUnitBridge
145145
PropertyAccess
146146
--------------
147147

148+
* Dropped support for booleans as the second argument of `PropertyAccessor::__construct()`.
149+
Pass a combination of bitwise flags instead.
148150
* Dropped support for booleans as the first argument of `PropertyAccessor::__construct()`.
149151
Pass a combination of bitwise flags instead.
150152

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,13 +1475,16 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui
14751475
$magicMethods |= $config['magic_get'] ? PropertyAccessor::MAGIC_GET : 0;
14761476
$magicMethods |= $config['magic_set'] ? PropertyAccessor::MAGIC_SET : 0;
14771477

1478+
$throw = PropertyAccessor::DO_NOT_THROW;
1479+
$throw |= $config['throw_exception_on_invalid_index'] ? PropertyAccessor::THROW_ON_INVALID_INDEX : 0;
1480+
$throw |= $config['throw_exception_on_invalid_property_path'] ? PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH : 0;
1481+
14781482
$container
14791483
->getDefinition('property_accessor')
14801484
->replaceArgument(0, $magicMethods)
1481-
->replaceArgument(1, $config['throw_exception_on_invalid_index'])
1482-
->replaceArgument(3, $config['throw_exception_on_invalid_property_path'])
1483-
->replaceArgument(4, new Reference(PropertyReadInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
1484-
->replaceArgument(5, new Reference(PropertyWriteInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
1485+
->replaceArgument(1, $throw)
1486+
->replaceArgument(3, new Reference(PropertyReadInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
1487+
->replaceArgument(4, new Reference(PropertyWriteInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
14851488
;
14861489
}
14871490

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@
1919
->set('property_accessor', PropertyAccessor::class)
2020
->args([
2121
abstract_arg('magic methods allowed, set by the extension'),
22-
abstract_arg('throwExceptionOnInvalidIndex, set by the extension'),
22+
abstract_arg('throw exceptions, set by the extension'),
2323
service('cache.property_access')->ignoreOnInvalid(),
24-
abstract_arg('throwExceptionOnInvalidPropertyPath, set by the extension'),
2524
abstract_arg('propertyReadInfoExtractor, set by the extension'),
2625
abstract_arg('propertyWriteInfoExtractor, set by the extension'),
2726
])

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,15 @@ public function testPropertyAccessWithDefaultValue()
9292

9393
$def = $container->getDefinition('property_accessor');
9494
$this->assertSame(PropertyAccessor::MAGIC_SET | PropertyAccessor::MAGIC_GET, $def->getArgument(0));
95-
$this->assertFalse($def->getArgument(1));
96-
$this->assertTrue($def->getArgument(3));
95+
$this->assertSame(PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, $def->getArgument(1));
9796
}
9897

9998
public function testPropertyAccessWithOverriddenValues()
10099
{
101100
$container = $this->createContainerFromFile('property_accessor');
102101
$def = $container->getDefinition('property_accessor');
103102
$this->assertSame(PropertyAccessor::MAGIC_GET | PropertyAccessor::MAGIC_CALL, $def->getArgument(0));
104-
$this->assertTrue($def->getArgument(1));
105-
$this->assertFalse($def->getArgument(3));
103+
$this->assertSame(PropertyAccessor::THROW_ON_INVALID_INDEX, $def->getArgument(1));
106104
}
107105

108106
public function testPropertyAccessCache()

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
"symfony/messenger": "<4.4",
8585
"symfony/mime": "<4.4",
8686
"symfony/property-info": "<4.4",
87-
"symfony/property-access": "<5.2",
87+
"symfony/property-access": "<5.3",
8888
"symfony/serializer": "<5.2",
8989
"symfony/stopwatch": "<4.4",
9090
"symfony/translation": "<5.0",

src/Symfony/Component/PropertyAccess/CHANGELOG.md

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

4+
5.3.0
5+
-----
6+
7+
* deprecated passing a boolean as the second argument of `PropertyAccessor::__construct()`, expecting a combination of bitwise flags instead
8+
49
5.2.0
510
-----
611

src/Symfony/Component/PropertyAccess/PropertyAccessor.php

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ class PropertyAccessor implements PropertyAccessorInterface
4747
/** @var int Allow magic __call methods */
4848
public const MAGIC_CALL = ReflectionExtractor::ALLOW_MAGIC_CALL;
4949

50+
public const DO_NOT_THROW = 0;
51+
public const THROW_ON_INVALID_INDEX = 1;
52+
public const THROW_ON_INVALID_PROPERTY_PATH = 2;
53+
5054
private const VALUE = 0;
5155
private const REF = 1;
5256
private const IS_REF_CHAINED = 2;
@@ -82,11 +86,15 @@ class PropertyAccessor implements PropertyAccessorInterface
8286
* Should not be used by application code. Use
8387
* {@link PropertyAccess::createPropertyAccessor()} instead.
8488
*
85-
* @param int $magicMethods A bitwise combination of the MAGIC_* constants
86-
* to specify the allowed magic methods (__get, __set, __call)
87-
* or self::DISALLOW_MAGIC_METHODS for none
89+
* @param int $magicMethods A bitwise combination of the MAGIC_* constants
90+
* to specify the allowed magic methods (__get, __set, __call)
91+
* or self::DISALLOW_MAGIC_METHODS for none
92+
* @param int $throw A bitwise combination of the THROW_* constants
93+
* to specify when exceptions should be thrown
94+
* @param PropertyReadInfoExtractorInterface $readInfoExtractor
95+
* @param PropertyWriteInfoExtractorInterface $writeInfoExtractor
8896
*/
89-
public function __construct(/*int */$magicMethods = self::MAGIC_GET | self::MAGIC_SET, bool $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null, bool $throwExceptionOnInvalidPropertyPath = true, PropertyReadInfoExtractorInterface $readInfoExtractor = null, PropertyWriteInfoExtractorInterface $writeInfoExtractor = null)
97+
public function __construct(/*int */$magicMethods = self::MAGIC_GET | self::MAGIC_SET, /*int */$throw = self::THROW_ON_INVALID_PROPERTY_PATH, CacheItemPoolInterface $cacheItemPool = null, /*PropertyReadInfoExtractorInterface */$readInfoExtractor = null, /*PropertyWriteInfoExtractorInterface */$writeInfoExtractor = null)
9098
{
9199
if (\is_bool($magicMethods)) {
92100
trigger_deprecation('symfony/property-access', '5.2', 'Passing a boolean as the first argument to "%s()" is deprecated. Pass a combination of bitwise flags instead (i.e an integer).', __METHOD__);
@@ -96,10 +104,39 @@ public function __construct(/*int */$magicMethods = self::MAGIC_GET | self::MAGI
96104
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be an integer, "%s" given.', __METHOD__, get_debug_type($readInfoExtractor)));
97105
}
98106

107+
if (\is_bool($throw)) {
108+
trigger_deprecation('symfony/property-access', '5.3', 'Passing a boolean as the second argument to "%s()" is deprecated. Pass a combination of bitwise flags instead (i.e an integer).', __METHOD__);
109+
110+
$throw = $throw ? self::THROW_ON_INVALID_INDEX : self::DO_NOT_THROW;
111+
112+
if (!\is_bool($readInfoExtractor)) {
113+
$throw |= self::THROW_ON_INVALID_PROPERTY_PATH;
114+
}
115+
}
116+
117+
if (\is_bool($readInfoExtractor)) {
118+
trigger_deprecation('symfony/property-access', '5.3', 'Passing a boolean as the fourth argument to "%s()" is deprecated. Pass a combination of bitwise flags as the second argument instead (i.e an integer).', __METHOD__);
119+
120+
if ($readInfoExtractor) {
121+
$throw |= self::THROW_ON_INVALID_PROPERTY_PATH;
122+
}
123+
124+
$readInfoExtractor = $writeInfoExtractor;
125+
$writeInfoExtractor = 4 < \func_num_args() ? func_get_arg(4) : null;
126+
}
127+
128+
if (null !== $readInfoExtractor && !$readInfoExtractor instanceof PropertyReadInfoExtractorInterface) {
129+
throw new \TypeError(sprintf('Argument 4 passed to "%s()" must be null or an instance of "%s", "%s" given.', __METHOD__, PropertyReadInfoExtractorInterface::class, get_debug_type($readInfoExtractor)));
130+
}
131+
132+
if (null !== $writeInfoExtractor && !$writeInfoExtractor instanceof PropertyWriteInfoExtractorInterface) {
133+
throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be null or an instance of "%s", "%s" given.', __METHOD__, PropertyWriteInfoExtractorInterface::class, get_debug_type($writeInfoExtractor)));
134+
}
135+
99136
$this->magicMethodsFlags = $magicMethods;
100-
$this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex;
137+
$this->ignoreInvalidIndices = 0 === ($throw & self::THROW_ON_INVALID_INDEX);
101138
$this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool; // Replace the NullAdapter by the null value
102-
$this->ignoreInvalidProperty = !$throwExceptionOnInvalidPropertyPath;
139+
$this->ignoreInvalidProperty = 0 === ($throw & self::THROW_ON_INVALID_PROPERTY_PATH);
103140
$this->readInfoExtractor = $readInfoExtractor ?? new ReflectionExtractor([], null, null, false);
104141
$this->writeInfoExtractor = $writeInfoExtractor ?? new ReflectionExtractor(['set'], null, null, false);
105142
}

src/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,16 @@ public function getWriteInfoExtractor(): ?PropertyWriteInfoExtractorInterface
283283
*/
284284
public function getPropertyAccessor()
285285
{
286-
return new PropertyAccessor($this->magicMethods, $this->throwExceptionOnInvalidIndex, $this->cacheItemPool, $this->throwExceptionOnInvalidPropertyPath, $this->readInfoExtractor, $this->writeInfoExtractor);
286+
$throw = PropertyAccessor::DO_NOT_THROW;
287+
288+
if ($this->throwExceptionOnInvalidIndex) {
289+
$throw |= PropertyAccessor::THROW_ON_INVALID_INDEX;
290+
}
291+
292+
if ($this->throwExceptionOnInvalidPropertyPath) {
293+
$throw |= PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH;
294+
}
295+
296+
return new PropertyAccessor($this->magicMethods, $throw, $this->cacheItemPool, $this->readInfoExtractor, $this->writeInfoExtractor);
287297
}
288298
}

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

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\PropertyAccess\Tests;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
1516
use Symfony\Component\Cache\Adapter\ArrayAdapter;
1617
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
1718
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
@@ -35,6 +36,8 @@
3536

3637
class PropertyAccessorTest extends TestCase
3738
{
39+
use ExpectDeprecationTrait;
40+
3841
/**
3942
* @var PropertyAccessor
4043
*/
@@ -112,7 +115,20 @@ public function testGetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $p
112115
*/
113116
public function testGetValueReturnsNullIfPropertyNotFoundAndExceptionIsDisabled($objectOrArray, $path)
114117
{
115-
$this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()->disableExceptionOnInvalidPropertyPath()->getPropertyAccessor();
118+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::MAGIC_GET | PropertyAccessor::MAGIC_SET, PropertyAccessor::DO_NOT_THROW);
119+
120+
$this->assertNull($this->propertyAccessor->getValue($objectOrArray, $path), $path);
121+
}
122+
123+
/**
124+
* @group legacy
125+
* @dataProvider getPathsWithMissingProperty
126+
*/
127+
public function testGetValueReturnsNullIfPropertyNotFoundAndExceptionIsDisabledUsingBooleanArgument($objectOrArray, $path)
128+
{
129+
$this->expectDeprecation('Since symfony/property-access 5.3: Passing a boolean as the fourth argument to "Symfony\Component\PropertyAccess\PropertyAccessor::__construct()" is deprecated. Pass a combination of bitwise flags as the second argument instead (i.e an integer).');
130+
131+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::MAGIC_GET | PropertyAccessor::MAGIC_SET, PropertyAccessor::DO_NOT_THROW, null, false);
116132

117133
$this->assertNull($this->propertyAccessor->getValue($objectOrArray, $path), $path);
118134
}
@@ -131,6 +147,19 @@ public function testGetValueThrowsNoExceptionIfIndexNotFound($objectOrArray, $pa
131147
public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
132148
{
133149
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException');
150+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
151+
$this->propertyAccessor->getValue($objectOrArray, $path);
152+
}
153+
154+
/**
155+
* @group legacy
156+
* @dataProvider getPathsWithMissingIndex
157+
*/
158+
public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnabledUsingBooleanArgument($objectOrArray, $path)
159+
{
160+
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException');
161+
$this->expectDeprecation('Since symfony/property-access 5.3: Passing a boolean as the second argument to "Symfony\Component\PropertyAccess\PropertyAccessor::__construct()" is deprecated. Pass a combination of bitwise flags instead (i.e an integer).');
162+
134163
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
135164
$this->propertyAccessor->getValue($objectOrArray, $path);
136165
}
@@ -253,7 +282,7 @@ public function testGetValueNotModifyObject()
253282

254283
public function testGetValueNotModifyObjectException()
255284
{
256-
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
285+
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
257286
$object = new \stdClass();
258287
$object->firstName = ['Bernhard'];
259288

@@ -341,7 +370,7 @@ public function testSetValueThrowsNoExceptionIfIndexNotFound($objectOrArray, $pa
341370
*/
342371
public function testSetValueThrowsNoExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
343372
{
344-
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
373+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
345374
$this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
346375

347376
$this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path));
@@ -428,7 +457,7 @@ public function testSetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $p
428457

429458
public function testGetValueWhenArrayValueIsNull()
430459
{
431-
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
460+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
432461
$this->assertNull($this->propertyAccessor->getValue(['index' => ['nullable' => null]], '[index][nullable]'));
433462
}
434463

@@ -462,7 +491,7 @@ public function testIsReadableReturnsTrueIfIndexNotFound($objectOrArray, $path)
462491
*/
463492
public function testIsReadableReturnsFalseIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
464493
{
465-
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
494+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
466495

467496
// When exceptions are enabled, non-existing indices cannot be read
468497
$this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path));
@@ -534,7 +563,7 @@ public function testIsWritableReturnsTrueIfIndexNotFound($objectOrArray, $path)
534563
*/
535564
public function testIsWritableReturnsTrueIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
536565
{
537-
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
566+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
538567

539568
// Non-existing indices can be written even if exceptions are enabled
540569
$this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path));
@@ -718,7 +747,7 @@ public function testCacheReadAccess()
718747
{
719748
$obj = new TestClass('foo');
720749

721-
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, false, new ArrayAdapter());
750+
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, new ArrayAdapter());
722751
$this->assertEquals('foo', $propertyAccessor->getValue($obj, 'publicGetSetter'));
723752
$propertyAccessor->setValue($obj, 'publicGetSetter', 'bar');
724753
$propertyAccessor->setValue($obj, 'publicGetSetter', 'baz');
@@ -732,7 +761,7 @@ public function testAttributeWithSpecialChars()
732761
$obj->{'a/b'} = '1';
733762
$obj->{'a%2Fb'} = '2';
734763

735-
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, false, new ArrayAdapter());
764+
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, new ArrayAdapter());
736765
$this->assertSame('bar', $propertyAccessor->getValue($obj, '@foo'));
737766
$this->assertSame('1', $propertyAccessor->getValue($obj, 'a/b'));
738767
$this->assertSame('2', $propertyAccessor->getValue($obj, 'a%2Fb'));
@@ -753,7 +782,7 @@ public function testAnonymousClassRead()
753782

754783
$obj = $this->generateAnonymousClass($value);
755784

756-
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, false, new ArrayAdapter());
785+
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, new ArrayAdapter());
757786

758787
$this->assertEquals($value, $propertyAccessor->getValue($obj, 'foo'));
759788
}
@@ -781,7 +810,7 @@ public function testAnonymousClassWrite()
781810

782811
$obj = $this->generateAnonymousClass('');
783812

784-
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, false, new ArrayAdapter());
813+
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, new ArrayAdapter());
785814
$propertyAccessor->setValue($obj, 'foo', $value);
786815

787816
$this->assertEquals($value, $propertyAccessor->getValue($obj, 'foo'));

0 commit comments

Comments
 (0)
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