8000 use bitwise flags to configure when the property accessor should throw · symfony/symfony@a50cfcb · GitHub
[go: up one dir, main page]

Skip to content

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a50cfcb

Browse files
committed
use bitwise flags to configure when the property accessor should throw
1 parent 6dd2d7b commit a50cfcb

File tree

10 files changed

+115
-28
lines changed

10 files changed

+115
-28
lines changed

UPGRADE-5.3.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ PhpunitBridge
6767

6868
* Deprecated the `SetUpTearDownTrait` trait, use original methods with "void" return typehint
6969

70+
PropertyAccess
71+
--------------
72+
73+
* Deprecate passing a boolean as the second argument of `PropertyAccessor::__construct()`, pass a combination of bitwise flags instead.
74+
7075
PropertyInfo
7176
------------
7277

UPGRADE-6.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ PhpUnitBridge
152152
PropertyAccess
153153
--------------
154154

155+
* Drop support for booleans as the second argument of `PropertyAccessor::__construct()`, pass a combination of bitwise flags instead.
155156
* Dropped support for booleans as the first argument of `PropertyAccessor::__construct()`.
156157
Pass a combination of bitwise flags instead.
157158

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

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

1576+
$throw = PropertyAccessor::DO_NOT_THROW;
1577+
$throw |= $config['throw_exception_on_invalid_index'] ? PropertyAccessor::THROW_ON_INVALID_INDEX : 0;
1578+
$throw |= $config['throw_exception_on_invalid_property_path'] ? PropertyAccessor::THROW_ON_INVALID_PROPERTY_PATH : 0;
1579+
15761580
$container
15771581
->getDefinition('property_accessor')
15781582
->replaceArgument(0, $magicMethods)
1579-
->replaceArgument(1, $config['throw_exception_on_invalid_index'])
1580-
->replaceArgument(3, $config['throw_exception_on_invalid_property_path'])
1581-
->replaceArgument(4, new Reference(PropertyReadInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
1582-
->replaceArgument(5, new Reference(PropertyWriteInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
1583+
->replaceArgument(1, $throw)
1584+
->replaceArgument(3, new Reference(PropertyReadInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
1585+
->replaceArgument(4, new Reference(PropertyWriteInfoExtractorInterface::class, ContainerInterface::NULL_ON_INVALID_REFERENCE))
15831586
;
15841587
}
15851588

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

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

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

116114
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/security-csrf": "<5.3",
9090
"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+
* deprecate 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)
0