Skip to content

Commit 8cf922c

Browse files
committed
feature symfony#18337 [PropertyInfo] Support singular adder and remover (dunglas)
This PR was squashed before being merged into the 3.2-dev branch (closes symfony#18337). Discussion ---------- [PropertyInfo] Support singular adder and remover | Q | A | ------------- | --- | Branch? | master | Bug fix? | yes | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | symfony#18166 | License | MIT | Doc PR | n/a Fix symfony#18166 with only a soft dependency to the PropertyAccess component. If symfony#18260 is accepted, this PR can be reworked to use this new component, in the meantime it fixes the problem with a soft dependency. I don't now if it should be considered as a bug fix (and then merged in 2.8) or as a new feature. It's technically a bug fix but I'm not sure that introducing a new soft dependency is acceptable if older branches. What do you think @symfony/deciders? Commits ------- 4cbb60c [PropertyInfo] Support singular adder and remover
2 parents 052c314 + 4cbb60c commit 8cf922c

File tree

4 files changed

+77
-13
lines changed

4 files changed

+77
-13
lines changed

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

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\PropertyInfo\Extractor;
1313

14+
use Symfony\Component\Inflector\Inflector;
1415
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
1516
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
1617
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
@@ -55,13 +56,17 @@ public function getProperties($class, array $context = array())
5556
return;
5657
}
5758

59+
$reflectionProperties = $reflectionClass->getProperties();
60+
5861
$properties = array();
59-
foreach ($reflectionClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflectionProperty) {
60-
$properties[$reflectionProperty->name] = true;
62+
foreach ($reflectionProperties as $reflectionProperty) {
63+
if ($reflectionProperty->isPublic()) {
64+
$properties[$reflectionProperty->name] = true;
65+
}
6166
}
6267

6368
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
64-
$propertyName = $this->getPropertyName($reflectionMethod->name);
69+
$propertyName = $this->getPropertyName($reflectionMethod->name, $reflectionProperties);
6570
if (!$propertyName || isset($properties[$propertyName])) {
6671
continue;
6772
}
@@ -310,33 +315,54 @@ private function getAccessorMethod($class, $property)
310315
private function getMutatorMethod($class, $property)
311316
{
312317
$ucProperty = ucfirst($property);
318+
$ucSingulars = (array) Inflector::singularize($ucProperty);
313319

314320
foreach (self::$mutatorPrefixes as $prefix) {
315-
try {
316-
$reflectionMethod = new \ReflectionMethod($class, $prefix.$ucProperty);
321+
$names = array($ucProperty);
322+
if (in_array($prefix, self::$arrayMutatorPrefixes)) {
323+
$names = array_merge($names, $ucSingulars);
324+
}
317325

318-
// Parameter can be optional to allow things like: method(array $foo = null)
319-
if ($reflectionMethod->getNumberOfParameters() >= 1) {
320-
return array($reflectionMethod, $prefix);
326+
foreach ($names as $name) {
327+
try {
328+
$reflectionMethod = new \ReflectionMethod($class, $prefix.$name);
329+
330+
// Parameter can be optional to allow things like: method(array $foo = null)
331+
if ($reflectionMethod->getNumberOfParameters() >= 1) {
332+
return array($reflectionMethod, $prefix);
333+
}
334+
} catch (\ReflectionException $reflectionException) {
335+
// Try the next one if method does not exist
321336
}
322-
} catch (\ReflectionException $reflectionException) {
323-
// Try the next prefix if the method doesn't exist
324337
}
325338
}
326339
}
327340

328341
/**
329342
* Extracts a property name from a method name.
330343
*
331-
* @param string $methodName
344+
* @param string $methodName
345+
* @param \ReflectionProperty[] $reflectionProperties
332346
*
333347
* @return string
334348
*/
335-
private function getPropertyName($methodName)
349+
private function getPropertyName($methodName, array $reflectionProperties)
336350
{
337351
$pattern = implode('|', array_merge(self::$accessorPrefixes, self::$mutatorPrefixes));
338352

339353
if (preg_match('/^('.$pattern.')(.+)$/i', $methodName, $matches)) {
354+
if (!in_array($matches[1], self::$arrayMutatorPrefixes)) {
355+
return $matches[2];
356+
}
357+
358+
foreach ($reflectionProperties as $reflectionProperty) {
359+
foreach ((array) Inflector::singularize($reflectionProperty->name) as $name) {
360+
if (strtolower($name) === strtolower($matches[2])) {
361+
return $reflectionProperty->name;
362+
}
363+
}
364+
}
365+
340366
return $matches[2];
341367
}
342368
}

src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\PropertyInfo\Tests\Extractor;
1313

1414
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
15+
use Symfony\Component\PropertyInfo\Tests\Fixtures\AdderRemoverDummy;
1516
use Symfony\Component\PropertyInfo\Type;
1617

1718
/**
@@ -119,4 +120,11 @@ public function testIsWritable()
119120
$this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'e', array()));
120121
$this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'f', array()));
121122
}
123+
124+
public function testSingularize()
125+
{
126+
$this->assertTrue($this->extractor->isWritable(AdderRemoverDummy::class, 'analyses'));
127+
$this->assertTrue($this->extractor->isWritable(AdderRemoverDummy::class, 'feet'));
128+
$this->assertEquals(array('analyses', 'feet'), $this->extractor->getProperties(AdderRemoverDummy::class));
129+
}
122130
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
13+
14+
/**
15+
* @author Kévin Dunglas <[email protected]>
16+
*/
17+
class AdderRemoverDummy
18+
{
19+
private $analyses;
20+
private $feet;
21+
22+
public function addAnalyse(Dummy $analyse)
23+
{
24+
}
25+
26+
public function removeFoot(Dummy $foot)
27+
{
28+
}
29+
}

src/Symfony/Component/PropertyInfo/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
}
2424
],
2525
"require": {
26-
"php": ">=5.5.9"
26+
"php": ">=5.5.9",
27+
"symfony/inflector": "~3.1"
2728
},
2829
"require-dev": {
2930
"symfony/serializer": "~2.8|~3.0",

0 commit comments

Comments
 (0)