Skip to content

Commit 2e83f22

Browse files
committed
use bitwise flags to configure when the property accessor should throw
1 parent 20bb3cb commit 2e83f22

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
@@ -61,6 +61,12 @@ PhpunitBridge
6161

6262
* Deprecated the `SetUpTearDownTrait` trait, use original methods with "void" return typehint
6363

64+
PropertyAccess
65+
--------------
66+
67+
* Deprecated passing a boolean as the second argument of `PropertyAccessor::__construct()`.
68+
Pass a combination of bitwise flags instead.
69+
6470
PropertyInfo
6571
------------
6672

UPGRADE-6.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ PhpUnitBridge
147147
PropertyAccess
148148
--------------
149149

150+
* Dropped support for booleans as the second argument of `PropertyAccessor::__construct()`.
151+
Pass a combination of bitwise flags instead.
150152
* Dropped support for booleans as the first argument of `PropertyAccessor::__construct()`.
151153
Pass a combination of bitwise flags instead.
152154

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

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

1489+
$throw = PropertyAccessor::DO_NOT_THROW;
1490+
$throw |= $config['throw_exception_on_invalid_index'] ? PropertyAccessor::THROW_ON_INVALID_INDEX : 0;
1491+
$throw |= $config['throw_exception_on_invalid_property_path'] ? PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH : 0;
1492+
14891493
$container
14901494
->getDefinition('property_accessor')
14911495
->replaceArgument(0, $magicMethods)
1492-
->replaceArgument(1, $config['throw_exception_on_invalid_index'])
1493-
->replaceArgument(3, $config['throw_exception_on_invalid_property_path'])
1494-
->replaceArgument(4, new Reference(PropertyReadInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
1495-
->replaceArgument(5, new Reference(PropertyWriteInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
1496+
->replaceArgument(1, $throw)
1497+
->replaceArgument(3, new Reference(PropertyReadInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
1498+
->replaceArgument(4, new Reference(PropertyWriteInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
14961499
;
14971500
}
14981501

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
@@ -99,17 +99,15 @@ public function testPropertyAccessWithDefaultValue()
9999

100100
$def = $container->getDefinition('property_accessor');
101101
$this->assertSame(PropertyAccessor::MAGIC_SET | PropertyAccessor::MAGIC_GET, $def->getArgument(0));
102-
$this->assertFalse($def->getArgument(1));
103-
$this->assertTrue($def->getArgument(3));
102+
$this->assertSame(PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, $def->getArgument(1));
104103
}
105104

106105
public function testPropertyAccessWithOverriddenValues()
107106
{
108107
$container = $this->createContainerFromFile('property_accessor');
109108
$def = $container->getDefinition('property_accessor');
110109
$this->assertSame(PropertyAccessor::MAGIC_GET | PropertyAccessor::MAGIC_CALL, $def->getArgument(0));
111-
$this->assertTrue($def->getArgument(1));
112-
$this->assertFalse($def->getArgument(3));
110+
$this->assertSame(PropertyAccessor::THROW_ON_INVALID_INDEX, $def->getArgument(1));
113111
}
114112

115113
public function testPropertyAccessCache()

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
"symfony/messenger": "<4.4",
8484
"symfony/mime": "<4.4",
8585
"symfony/property-info": "<4.4",
86-
"symfony/property-access": "<5.2",
86+
"symfony/property-access": "<5.3",
8787
"symfony/serializer": "<5.2",
8888
"symfony/security-csrf": "<5.3",
8989
"symfony/security-core": "<5.3",

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\AccessException;
1718
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
@@ -38,6 +39,8 @@
3839

3940
class PropertyAccessorTest extends TestCase
4041
{
42+
use ExpectDeprecationTrait;
43+
4144
/**
4245
* @var PropertyAccessor
4346
*/
@@ -115,7 +118,20 @@ public function testGetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $p
115118
*/
116119
public function testGetValueReturnsNullIfPropertyNotFoundAndExceptionIsDisabled($objectOrArray, $path)
117120
{
118-
$this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()->disableExceptionOnInvalidPropertyPath()->getPropertyAccessor();
121+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::MAGIC_GET | PropertyAccessor::MAGIC_SET, PropertyAccessor::DO_NOT_THROW);
122+
123+
$this->assertNull($this->propertyAccessor->getValue($objectOrArray, $path), $path);
124+
}
125+
126+
/**
127+
* @group legacy
128+
* @dataProvider getPathsWithMissingProperty
129+
*/
130+
public function testGetValueReturnsNullIfPropertyNotFoundAndExceptionIsDisabledUsingBooleanArgument($objectOrArray, $path)
131+
{
132+
$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).');
133+
134+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::MAGIC_GET | PropertyAccessor::MAGIC_SET, PropertyAccessor::DO_NOT_THROW, null, false);
119135

120136
$this->assertNull($this->propertyAccessor->getValue($objectOrArray, $path), $path);
121137
}
@@ -134,6 +150,19 @@ public function testGetValueThrowsNoExceptionIfIndexNotFound($objectOrArray, $pa
134150
public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
135151
{
136152
$this->expectException(NoSuchIndexException::class);
153+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
154+
$this->propertyAccessor->getValue($objectOrArray, $path);
155+
}
156+
157+
/**
158+
* @group legacy
159+
* @dataProvider getPathsWithMissingIndex
160+
*/
161+
public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnabledUsingBooleanArgument($objectOrArray, $path)
162+
{
163+
$this->expectException(NoSuchIndexException::class);
164+
$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).');
165+
137166
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
138167
$this->propertyAccessor->getValue($objectOrArray, $path);
139168
}
@@ -256,7 +285,7 @@ public function testGetValueNotModifyObject()
256285

257286
public function testGetValueNotModifyObjectException()
258287
{
259-
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
288+
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
260289
$object = new \stdClass();
261290
$object->firstName = ['Bernhard'];
262291

@@ -344,7 +373,7 @@ public function testSetValueThrowsNoExceptionIfIndexNotFound($objectOrArray, $pa
344373
*/
345374
public function testSetValueThrowsNoExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
346375
{
347-
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
376+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
348377
$this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
349378

350379
$this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path));
@@ -431,7 +460,7 @@ public function testSetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $p
431460

432461
public function testGetValueWhenArrayValueIsNull()
433462
{
434-
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
463+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
435464
$this->assertNull($this->propertyAccessor->getValue(['index' => ['nullable' => null]], '[index][nullable]'));
436465
}
437466

@@ -465,7 +494,7 @@ public function testIsReadableReturnsTrueIfIndexNotFound($objectOrArray, $path)
465494
*/
466495
public function testIsReadableReturnsFalseIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
467496
{
468-
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
497+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
469498

470499
// When exceptions are enabled, non-existing indices cannot be read
471500
$this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path));
@@ -537,7 +566,7 @@ public function testIsWritableReturnsTrueIfIndexNotFound($objectOrArray, $path)
537566
*/
538567
public function testIsWritableReturnsTrueIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
539568
{
540-
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, true);
569+
$this->propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_INDEX | PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH);
541570

542571
// Non-existing indices can be written even if exceptions are enabled
543572
$this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path));
@@ -721,7 +750,7 @@ public function testCacheReadAccess()
721750
{
722751
$obj = new TestClass('foo');
723752

724-
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, false, new ArrayAdapter());
753+
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, new ArrayAdapter());
725754
$this->assertEquals('foo', $propertyAccessor->getValue($obj, 'publicGetSetter'));
726755
$propertyAccessor->setValue($obj, 'publicGetSetter', 'bar');
727756
$propertyAccessor->setValue($obj, 'publicGetSetter', 'baz');
@@ -735,7 +764,7 @@ public function testAttributeWithSpecialChars()
735764
$obj->{'a/b'} = '1';
736765
$obj->{'a%2Fb'} = '2';
737766

738-
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, false, new ArrayAdapter());
767+
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, new ArrayAdapter());
739768
$this->assertSame('bar', $propertyAccessor->getValue($obj, '@foo'));
740769
$this->assertSame('1', $propertyAccessor->getValue($obj, 'a/b'));
741770
$this->assertSame('2', $propertyAccessor->getValue($obj, 'a%2Fb'));
@@ -756,7 +785,7 @@ public function testAnonymousClassRead()
756785

757786
$obj = $this->generateAnonymousClass($value);
758787

759-
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, false, new ArrayAdapter());
788+
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, new ArrayAdapter());
760789

761790
$this->assertEquals($value, $propertyAccessor->getValue($obj, 'foo'));
762791
}
@@ -784,7 +813,7 @@ public function testAnonymousClassWrite()
784813

785814
$obj = $this->generateAnonymousClass('');
786815

787-
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, false, new ArrayAdapter());
816+
$propertyAccessor = new PropertyAccessor(PropertyAccessor::DISALLOW_MAGIC_METHODS, PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH, new ArrayAdapter());
788817
$propertyAccessor->setValue($obj, 'foo', $value);
789818

790819
$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