Skip to content

Navigation Menu

Sign in
Appearance settings

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 f1c69ad

Browse filesBrowse files
committed
Use property info in property access to retrieve accessor / mutator, add specific context to allow BC
1 parent 3e392b9 commit f1c69ad
Copy full SHA for f1c69ad

File tree

8 files changed

+304
-369
lines changed
Filter options

8 files changed

+304
-369
lines changed

‎src/Symfony/Component/PropertyAccess/PropertyAccessor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/PropertyAccessor.php
+81-245Lines changed: 81 additions & 245 deletions
Large diffs are not rendered by default.

‎src/Symfony/Component/PropertyAccess/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/composer.json
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
],
1818
"require": {
1919
"php": "^7.1.3",
20-
"symfony/inflector": "~3.4|~4.0"
20+
"symfony/inflector": "~3.4|~4.0",
21+
"symfony/property-info": "~4.3"
2122
},
2223
"require-dev": {
2324
"symfony/cache": "~3.4|~4.0"

‎src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
+91-64Lines changed: 91 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -205,42 +205,46 @@ public function getReadAccessor(string $class, string $property, array $context
205205
return null;
206206
}
207207

208+
$allowGetterSetter = $context['enable_getter_setter_extraction'] ?? false;
209+
$allowMagicCall = $context['enable_magic_call_extraction'] ?? false;
210+
208211
$hasProperty = $reflClass->hasProperty($property);
209212
$camelProp = $this->camelize($property);
210213
$getsetter = lcfirst($camelProp); // jQuery style, e.g. read: last(), write: last($item)
211-
$accessPrivate = false;
212-
$accessStatic = false;
213-
$accessType = null;
214214

215215
foreach ($this->accessorPrefixes as $prefix) {
216-
$methodName = $prefix . $camelProp;
216+
$methodName = $prefix.$camelProp;
217217

218218
if ($reflClass->hasMethod($methodName) && ($reflClass->getMethod($methodName)->getModifiers() & $this->methodReflectionFlags)) {
219-
$accessType = ReadAccessor::TYPE_METHOD;
220-
$accessName = $methodName;
221-
$accessStatic = $reflClass->getMethod($methodName)->isStatic();
219+
$method = $reflClass->getMethod($methodName);
220+
221+
return ReadAccessor::createMethod($methodName, $method->isPrivate() || $method->isProtected(), $method->isStatic());
222222
}
223223
}
224224

225-
if (!$accessType) {
226-
if ($reflClass->hasMethod($getsetter) && ($reflClass->getMethod($getsetter)->getModifiers() & $this->methodReflectionFlags)) {
227-
$accessType = ReadAccessor::TYPE_METHOD;
228-
$accessName = $getsetter;
229-
$accessStatic = $reflClass->getMethod($getsetter)->isStatic();
230-
} elseif ($hasProperty && ($reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags)) {
231-
$accessType = ReadAccessor::TYPE_PROPERTY;
232-
$accessName = $property;
233-
$accessStatic = $reflClass->getProperty($property)->isStatic();
234-
$accessPrivate = !$reflClass->getProperty($property)->isPublic();
235-
} elseif ($reflClass->hasMethod('__get') && ($reflClass->getMethod('__get')->getModifiers() & $this->methodReflectionFlags)) {
236-
$accessType = ReadAccessor::TYPE_PROPERTY;
237-
$accessName = $property;
238-
} else {
239-
return null;
240-
}
225+
if ($allowGetterSetter && $reflClass->hasMethod($getsetter) && ($reflClass->getMethod($getsetter)->getModifiers() & $this->methodReflectionFlags)) {
226+
$method = $reflClass->getMethod($getsetter);
227+
228+
return ReadAccessor::createMethod($getsetter, $method->isPrivate() || $method->isProtected(), $method->isStatic());
229+
}
230+
231+
if ($hasProperty && ($reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags)) {
232+
$reflProperty = $reflClass->getProperty($property);
233+
234+
return ReadAccessor::createProperty($property, $reflProperty->isPrivate() || $reflProperty->isProtected(), $reflProperty->isStatic(), true);
235+
}
236+
237+
if ($reflClass->hasMethod('__get') && ($reflClass->getMethod('__get')->getModifiers() & $this->methodReflectionFlags)) {
238+
return ReadAccessor::createProperty($property, false, false, false);
239+
}
240+
241+
if ($allowMagicCall && $reflClass->hasMethod('__call') && ($reflClass->getMethod('__call')->getModifiers() & $this->methodReflectionFlags)) {
242+
$method = $reflClass->getMethod('__call');
243+
244+
return ReadAccessor::createMethod('get'.$camelProp, $method->isPrivate() || $method->isProtected(), $method->isStatic());
241245
}
242246

243-
return new ReadAccessor($accessType, $accessName, $accessPrivate, $accessStatic);
247+
return null;
244248
}
245249

246250
/**
@@ -254,57 +258,74 @@ public function getWriteMutator(string $class, string $property, array $context
254258
return null;
255259
}
256260

261+
$allowGetterSetter = $context['enable_getter_setter_extraction'] ?? false;
262+
$allowMagicCall = $context['enable_magic_call_extraction'] ?? false;
257263
$allowConstruct = $context['enable_constructor_extraction'] ?? $this->enableConstructorExtraction;
258-
$hasProperty = $reflClass->hasProperty($property);
264+
$allowAdderRemover = $context['enable_adder_remover_extraction'] ?? true;
265+
259266
$camelized = $this->camelize($property);
260-
$accessParameter = null;
261-
$accessName = null;
262-
$adderAccessName = null;
263-
$removerAccessName = null;
264-
$accessType = null;
265-
$accessPrivate = false;
266-
$accessStatic = false;
267267
$constructor = $reflClass->getConstructor();
268268

269-
if (null !== $constructor) {
269+
if (null !== $constructor && $allowConstruct) {
270270
foreach ($constructor->getParameters() as $parameter) {
271271
if ($parameter->getName() === $property) {
272-
$accessParameter = $parameter;
272+
return WriteMutator::createConstructor($property);
273273
}
274274
}
275275
}
276276

277-
$setter = 'set'.$camelized;
278-
$getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item)
279-
280-
if (null !== $accessParameter && $allowConstruct) {
281-
$accessType = WriteMutator::TYPE_CONSTRUCTOR;
282-
$accessName = $property;
283-
} elseif ($this->isMethodAccessible($reflClass, $setter, 1)) {
284-
$accessType = WriteMutator::TYPE_METHOD;
285-
$accessName = $setter;
286-
$accessStatic = $reflClass->getMethod($setter)->isStatic();
287-
} elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) {
288-
$accessType = WriteMutator::TYPE_METHOD;
289-
$accessName = $getsetter;
290-
$accessStatic = $reflClass->getMethod($getsetter)->isStatic();
291-
} elseif (null !== $methods = $this->findAdderAndRemover($reflClass, (array) Inflector::singularize($camelized))) {
277+
if ($allowAdderRemover && null !== $methods = $this->findAdderAndRemover($reflClass, (array) Inflector::singularize($camelized))) {
292278
$accessType = WriteMutator::TYPE_ADDER_REMOVER_METHOD;
293-
$adderAccessName = $methods[0];
294-
$removerAccessName = $methods[1];
295-
} elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
296-
$accessType = WriteMutator::TYPE_PROPERTY;
297-
$accessName = $property;
298-
} elseif ($hasProperty && ($reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags)) {
299-
$accessType = WriteMutator::TYPE_PROPERTY;
300-
$accessName = $property;
301-
$accessPrivate = !$reflClass->getProperty($property)->isPublic();
302-
$accessStatic = $reflClass->getProperty($property)->isStatic();
303-
} else {
304-
return null;
279+
[$adderAccessName, $removerAccessName] = $methods;
280+
281+
$adderMethod = $reflClass->getMethod($adderAccessName);
282+
$removerMethod = $reflClass->getMethod($removerAccessName);
283+
284+
return WriteMutator::createAdderRemoverMethod(
285+
WriteMutator::createMethod($adderAccessName, $adderMethod->isPrivate() || $adderMethod->isProtected(), $adderMethod->isStatic()),
286+
WriteMutator::createMethod($removerAccessName, $removerMethod->isPrivate() || $removerMethod->isProtected(), $removerMethod->isStatic())
287+
);
288+
}
289+
290+
foreach ($this->mutatorPrefixes as $mutatorPrefix) {
291+
$methodName = $mutatorPrefix.$camelized;
292+
293+
if (!$this->isMethodAccessible($reflClass, $methodName, 1)) {
294+
continue;
295+
}
296+
297+
$method = $reflClass->getMethod($methodName);
298+
299+
if (!\in_array($mutatorPrefix, $this->arrayMutatorPrefixes, true)) {
300+
return WriteMutator::createMethod($methodName, $method->isPrivate() || $method->isProtected(), $method->isStatic());
301+
}
302+
}
303+
304+
$getsetter = lcfirst($camelized);
305+
306+
if ($allowGetterSetter && $this->isMethodAccessible($reflClass, $getsetter, 1)) {
307+
$method = $reflClass->getMethod($getsetter);
308+
309+
return WriteMutator::createMethod($getsetter, $method->isPrivate() || $method->isProtected(), $method->isStatic());
310+
}
311+
312+
if ($reflClass->hasProperty($property) && ($reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags)) {
313+
$reflProperty = $reflClass->getProperty($property);
314+
315+
return WriteMutator::createProperty($property, $reflProperty->isPrivate() || $reflProperty->isProtected(), $reflProperty->isStatic());
316+
}
317+
318+
if ($this->isMethodAccessible($reflClass, '__set', 2)) {
319+
return WriteMutator::createProperty($property, false, false);
320+
}
321+
322+
if ($allowMagicCall && $this->isMethodAccessible($reflClass, '__call', 2)) {
323+
$method = $reflClass->getMethod('__call');
324+
325+
return WriteMutator::createMethod('set'.$camelized, $method->isPrivate() || $method->isProtected(), $method->isStatic());
305326
}
306327

307-
return new WriteMutator($accessType, $accessName, $adderAccessName, $removerAccessName, $accessPrivate, $accessStatic, $accessParameter);
328+
return null;
308329
}
309330

310331
/**
@@ -548,9 +569,15 @@ private function getPropertyName(string $methodName, array $reflectionProperties
548569
*/
549570
private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars)
550571
{
572+
if (!\is_array($this->arrayMutatorPrefixes) && 2 !== \count($this->arrayMutatorPrefixes)) {
573+
return null;
574+
}
575+
576+
[$addPrefix, $removePrefix] = $this->arrayMutatorPrefixes;
577+
551578
foreach ($singulars as $singular) {
552-
$addMethod = 'add'.$singular;
553-
$removeMethod = 'remove'.$singular;
579+
$addMethod = $addPrefix.$singular;
580+
$removeMethod = $removePrefix.$singular;
554581

555582
$addMethodFound = $this->isMethodAccessible($reflClass, $addMethod, 1);
556583
$removeMethodFound = $this->isMethodAccessible($reflClass, $removeMethod, 1);

‎src/Symfony/Component/PropertyInfo/ReadAccessor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/ReadAccessor.php
+42-8Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,14 @@
1212
namespace Symfony\Component\PropertyInfo;
1313

1414
/**
15-
* Read accessor tell how to read from a property.
15+
* The read accessor tells how a property can be read.
1616
*
1717
* @author Joel Wurtz <jwurtz@jolicode.com>
1818
*/
1919
final class ReadAccessor
2020
{
2121
public const TYPE_METHOD = 1;
2222
public const TYPE_PROPERTY = 2;
23-
public const TYPE_ARRAY_DIMENSION = 3;
24-
public const TYPE_SOURCE = 4;
2523

2624
private $type;
2725

@@ -31,19 +29,23 @@ final class ReadAccessor
3129

3230
private $static;
3331

34-
public function __construct(int $type, string $name, bool $private = false, bool $static = false)
32+
private $byRef;
33+
34+
private function __construct()
3535
{
36-
$this->type = $type;
37-
$this->name = $name;
38-
$this->private = $private;
39-
$this->static = $static;
4036
}
4137

38+
/**
39+
* Get type of access.
40+
*/
4241
public function getType(): int
4342
{
4443
return $this->type;
4544
}
4645

46+
/**
47+
* Get name of the access, which can be a method name or a property name, depending on the type.
48+
*/
4749
public function getName(): string
4850
{
4951
return $this->name;
@@ -58,4 +60,36 @@ public function isStatic(): bool
5860
{
5961
return $this->static;
6062
}
63+
64+
/**
65+
* Whether this accessor can be accessed by reference.
66+
*/
67+
public function canBeReference(): bool
68+
{
69+
return $this->byRef;
70+
}
71+
72+
public static function createProperty(string $propertyName, bool $private, bool $static, bool $byRef)
73+
{
74+
$accessor = new self();
75+
$accessor->type = self::TYPE_PROPERTY;
76+
$accessor->name = $propertyName;
77+
$accessor->private = $private;
78+
$accessor->static = $static;
79+
$accessor->byRef = $byRef;
80+
81+
return $accessor;
82+
}
83+
84+
public static function createMethod(string $methodName, bool $private, bool $static)
85+
{
86+
$accessor = new self();
87+
$accessor->type = self::TYPE_METHOD;
88+
$accessor->name = $methodName;
89+
$accessor->private = $private;
90+
$accessor->static = $static;
91+
$accessor->byRef = false;
92+
93+
return $accessor;
94+
}
6195
}

‎src/Symfony/Component/PropertyInfo/ReadAccessorExtractorInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/ReadAccessorExtractorInterface.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
namespace Symfony\Component\PropertyInfo;
1313

1414
/**
15-
* Extract read accessor for property of a class.
15+
* Extract read accessor information for the property of a class.
1616
*
1717
* @author Joel Wurtz <jwurtz@jolicode.com>
1818
*/

‎src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
+36-26Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -415,11 +415,12 @@ public function readAccessorProvider(): array
415415
/**
416416
* @dataProvider writeMutatorProvider
417417
*/
418-
public function testGetWriteMutator($class, $property, $allowConstruct, $found, $type, $name, $addName, $removeName, $private, $static, $hasParameter)
418+
public function testGetWriteMutator($class, $property, $allowConstruct, $found, $type, $name, $addName, $removeName, $private, $static)
419419
{
420420
$extractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PROTECTED | ReflectionExtractor::ALLOW_PRIVATE);
421421
$writeMutator = $extractor->getWriteMutator($class, $property, [
422422
'enable_constructor_extraction' => $allowConstruct,
423+
'enable_getter_setter_extraction' => true,
423424
]);
424425

425426
if (!$found) {
@@ -431,39 +432,48 @@ public function testGetWriteMutator($class, $property, $allowConstruct, $found,
431432
$this->assertNotNull($writeMutator);
432433
$this->assertSame($type, $writeMutator->getType());
433434
$this->assertSame($name, $writeMutator->getName());
434-
$this->assertSame($addName, $writeMutator->getAdderName());
435-
$this->assertSame($removeName, $writeMutator->getRemoverName());
436-
$this->assertSame($private, $writeMutator->isPrivate());
437-
$this->assertSame($static, $writeMutator->isStatic());
438435

439-
if ($hasParameter) {
440-
$this->assertNotNull($writeMutator->getConstructorParameter());
436+
if (null !== $addName) {
437+
$this->assertNotNull($writeMutator->getAdder());
438+
$this->assertSame($addName, $writeMutator->getAdder()->getName());
441439
} else {
442-
$this->assertNull($writeMutator->getConstructorParameter());
440+
$this->assertNull($writeMutator->getAdder());
441+
}
442+
443+
if (null !== $removeName) {
444+
$this->assertNotNull($writeMutator->getRemover());
445+
$this->assertSame($removeName, $writeMutator->getRemover()->getName());
446+
} else {
447+
$this->assertNull($writeMutator->getRemover());
448+
}
449+
450+
if (WriteMutator::TYPE_ADDER_REMOVER_METHOD !== $writeMutator->getType() && WriteMutator::TYPE_CONSTRUCTOR !== $writeMutator->getType()) {
451+
$this->assertSame($private, $writeMutator->isPrivate());
452+
$this->assertSame($static, $writeMutator->isStatic());
443453
}
444454
}
445455

446456
public function writeMutatorProvider(): array
447457
{
448458
return [
449-
[Dummy::class, 'bar', false, true, ReadAccessor::TYPE_PROPERTY, 'bar', null, null, true, false, false],
450-
[Dummy::class, 'baz', false, true, ReadAccessor::TYPE_PROPERTY, 'baz', null, null, true, false, false],
451-
[Dummy::class, 'bal', false, true, ReadAccessor::TYPE_PROPERTY, 'bal', null, null, false, false, false],
452-
[Dummy::class, 'parent', false, true, ReadAccessor::TYPE_PROPERTY, 'parent', null, null, false, false, false],
453-
[Dummy::class, 'staticSetter', false, true, ReadAccessor::TYPE_METHOD, 'staticSetter', null, null, false, true, false],
454-
[Dummy::class, 'foo', false, true, ReadAccessor::TYPE_PROPERTY, 'foo', null, null, false, false, false],
455-
[Php71Dummy::class, 'bar', false, true, WriteMutator::TYPE_METHOD, 'setBar', null, null, false, false, false],
456-
[Php71Dummy::class, 'string', false, false, '', '', null, null, false, false, false],
457-
[Php71Dummy::class, 'string', true, true, WriteMutator::TYPE_CONSTRUCTOR, 'string', null, null, false, false, true],
458-
[Php71Dummy::class, 'baz', false, true, WriteMutator::TYPE_ADDER_REMOVER_METHOD, null, 'addBaz', 'removeBaz', false, false, false],
459-
[Php71DummyExtended::class, 'bar', false, true, WriteMutator::TYPE_METHOD, 'setBar', null, null, false, false, false],
460-
[Php71DummyExtended::class, 'string', false, false, -1, '', null, null, false, false, false],
461-
[Php71DummyExtended::class, 'string', true, true, WriteMutator::TYPE_CONSTRUCTOR, 'string', null, null, false, false, true],
462-
[Php71DummyExtended::class, 'baz', false, true, WriteMutator::TYPE_ADDER_REMOVER_METHOD, null, 'addBaz', 'removeBaz', false, false, false],
463-
[Php71DummyExtended2::class, 'bar', false, true, WriteMutator::TYPE_METHOD, 'setBar', null, null, false, false, false],
464-
[Php71DummyExtended2::class, 'string', false, false, '', '', null, null, false, false, false],
465-
[Php71DummyExtended2::class, 'string', true, false, '', '', null, null, false, false, false],
466-
[Php71DummyExtended2::class, 'baz', false, true, WriteMutator::TYPE_ADDER_REMOVER_METHOD, null, 'addBaz', 'removeBaz', false, false, false],
459+
[Dummy::class, 'bar', false, true, ReadAccessor::TYPE_PROPERTY, 'bar', null, null, true, false],
460+
[Dummy::class, 'baz', false, true, ReadAccessor::TYPE_PROPERTY, 'baz', null, null, true, false],
461+
[Dummy::class, 'bal', false, true, ReadAccessor::TYPE_PROPERTY, 'bal', null, null, false, false],
462+
[Dummy::class, 'parent', false, true, ReadAccessor::TYPE_PROPERTY, 'parent', null, null, false, false],
463+
[Dummy::class, 'staticSetter', false, true, ReadAccessor::TYPE_METHOD, 'staticSetter', null, null, false, true],
464+
[Dummy::class, 'foo', false, true, ReadAccessor::TYPE_PROPERTY, 'foo', null, null, false, false],
465+
[Php71Dummy::class, 'bar', false, true, WriteMutator::TYPE_METHOD, 'setBar', null, null, false, false],
466+
[Php71Dummy::class, 'string', false, false, '', '', null, null, false, false],
467+
[Php71Dummy::class, 'string', true, true, WriteMutator::TYPE_CONSTRUCTOR, 'string', null, null, false, false],
468+
[Php71Dummy::class, 'baz', false, true, WriteMutator::TYPE_ADDER_REMOVER_METHOD, null, 'addBaz', 'removeBaz', false, false],
469+
[Php71DummyExtended::class, 'bar', false, true, WriteMutator::TYPE_METHOD, 'setBar', null, null, false, false],
470+
[Php71DummyExtended::class, 'string', false, false, -1, '', null, null, false, false],
471+
[Php71DummyExtended::class, 'string', true, true, WriteMutator::TYPE_CONSTRUCTOR, 'string', null, null, false, false],
472+
[Php71DummyExtended::class, 'baz', false, true, WriteMutator::TYPE_ADDER_REMOVER_METHOD, null, 'addBaz', 'removeBaz', false, false],
473+
[Php71DummyExtended2::class, 'bar', false, true, WriteMutator::TYPE_METHOD, 'setBar', null, null, false, false],
474+
[Php71DummyExtended2::class, 'string', false, false, '', '', null, null, false, false],
475+
[Php71DummyExtended2::class, 'string', true, false, '', '', null, null, false, false],
476+
[Php71DummyExtended2::class, 'baz', false, true, WriteMutator::TYPE_ADDER_REMOVER_METHOD, null, 'addBaz', 'removeBaz', false, false],
467477
];
468478
}
469479
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.