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

Skip to content
8000

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, $< 2FCB span class=pl-smi>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)
0