Skip to content

Commit

Permalink
feature #22696 [PropertyInfo] Made ReflectionExtractor's prefix lists…
Browse files Browse the repository at this point in the history
… instance variables (neemzy)

This PR was merged into the 3.4 branch.

Discussion
----------

[PropertyInfo] Made ReflectionExtractor's prefix lists instance variables

| Q             | A
| ------------- | ---
| Branch?       | `3.4`
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| License       | MIT

This PR makes `ReflectionExtractor`'s mutator/accessor prefixes instance variables in order to be able to override them to change its behavior.

Commits
-------

58e733b [PropertyInfo] Made ReflectionExtractor's prefix lists instance variables
  • Loading branch information
fabpot committed Jun 7, 2017
2 parents b0ede2c + 58e733b commit 384b34b
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 14 deletions.
30 changes: 27 additions & 3 deletions src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,35 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
*/
private $phpDocTypeHelper;

public function __construct(DocBlockFactoryInterface $docBlockFactory = null)
/**
* @var string[]
*/
private $mutatorPrefixes;

/**
* @var string[]
*/
private $accessorPrefixes;

/**
* @var string[]
*/
private $arrayMutatorPrefixes;

/**
* @param DocBlockFactoryInterface $docBlockFactory
* @param string[]|null $mutatorPrefixes
* @param string[]|null $accessorPrefixes
* @param string[]|null $arrayMutatorPrefixes
*/
public function __construct(DocBlockFactoryInterface $docBlockFactory = null, array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null)
{
$this->docBlockFactory = $docBlockFactory ?: DocBlockFactory::createInstance();
$this->contextFactory = new ContextFactory();
$this->phpDocTypeHelper = new PhpDocTypeHelper();
$this->mutatorPrefixes = null !== $mutatorPrefixes ? $mutatorPrefixes : ReflectionExtractor::$defaultMutatorPrefixes;
$this->accessorPrefixes = null !== $accessorPrefixes ? $accessorPrefixes : ReflectionExtractor::$defaultAccessorPrefixes;
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : ReflectionExtractor::$defaultArrayMutatorPrefixes;
}

/**
Expand Down Expand Up @@ -137,7 +161,7 @@ public function getTypes($class, $property, array $context = array())
return;
}

if (!in_array($prefix, ReflectionExtractor::$arrayMutatorPrefixes)) {
if (!in_array($prefix, $this->arrayMutatorPrefixes)) {
return $types;
}

Expand Down Expand Up @@ -217,7 +241,7 @@ private function getDocBlockFromProperty($class, $property)
*/
private function getDocBlockFromMethod($class, $ucFirstProperty, $type)
{
$prefixes = $type === self::ACCESSOR ? ReflectionExtractor::$accessorPrefixes : ReflectionExtractor::$mutatorPrefixes;
$prefixes = $type === self::ACCESSOR ? $this->accessorPrefixes : $this->mutatorPrefixes;
$prefix = null;

foreach ($prefixes as $prefix) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,53 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
*
* @var string[]
*/
public static $mutatorPrefixes = array('add', 'remove', 'set');
public static $defaultMutatorPrefixes = array('add', 'remove', 'set');

/**
* @internal
*
* @var string[]
*/
public static $accessorPrefixes = array('is', 'can', 'get');
public static $defaultAccessorPrefixes = array('is', 'can', 'get');

/**
* @internal
*
* @var string[]
*/
public static $arrayMutatorPrefixes = array('add', 'remove');
public static $defaultArrayMutatorPrefixes = array('add', 'remove');

/**
* @var bool
*/
private $supportsParameterType;

public function __construct()
/**
* @var string[]
*/
private $mutatorPrefixes;

/**
* @var string[]
*/
private $accessorPrefixes;

/**
* @var string[]
*/
private $arrayMutatorPrefixes;

/**
* @param string[]|null $mutatorPrefixes
* @param string[]|null $accessorPrefixes
* @param string[]|null $arrayMutatorPrefixes
*/
public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null)
{
$this->supportsParameterType = method_exists('ReflectionParameter', 'getType');
$this->mutatorPrefixes = null !== $mutatorPrefixes ? $mutatorPrefixes : self::$defaultMutatorPrefixes;
$this->accessorPrefixes = null !== $accessorPrefixes ? $accessorPrefixes : self::$defaultAccessorPrefixes;
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : self::$defaultArrayMutatorPrefixes;
}

/**
Expand Down Expand Up @@ -174,7 +200,7 @@ private function extractFromMutator($class, $property)
return;
}

if (in_array($prefix, self::$arrayMutatorPrefixes)) {
if (in_array($prefix, $this->arrayMutatorPrefixes)) {
$type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type);
}

Expand Down Expand Up @@ -266,7 +292,7 @@ private function getAccessorMethod($class, $property)
{
$ucProperty = ucfirst($property);

foreach (self::$accessorPrefixes as $prefix) {
foreach ($this->accessorPrefixes as $prefix) {
try {
$reflectionMethod = new \ReflectionMethod($class, $prefix.$ucProperty);
if ($reflectionMethod->isStatic()) {
Expand Down Expand Up @@ -298,9 +324,9 @@ private function getMutatorMethod($class, $property)
$ucProperty = ucfirst($property);
$ucSingulars = (array) Inflector::singularize($ucProperty);

foreach (self::$mutatorPrefixes as $prefix) {
foreach ($this->mutatorPrefixes as $prefix) {
$names = array($ucProperty);
if (in_array($prefix, self::$arrayMutatorPrefixes)) {
if (in_array($prefix, $this->arrayMutatorPrefixes)) {
$names = array_merge($names, $ucSingulars);
}

Expand Down Expand Up @@ -332,10 +358,10 @@ private function getMutatorMethod($class, $property)
*/
private function getPropertyName($methodName, array $reflectionProperties)
{
$pattern = implode('|', array_merge(self::$accessorPrefixes, self::$mutatorPrefixes));
$pattern = implode('|', array_merge($this->accessorPrefixes, $this->mutatorPrefixes));

if (preg_match('/^('.$pattern.')(.+)$/i', $methodName, $matches)) {
if (!in_array($matches[1], self::$arrayMutatorPrefixes)) {
if ('' !== $pattern && preg_match('/^('.$pattern.')(.+)$/i', $methodName, $matches)) {
if (!in_array($matches[1], $this->arrayMutatorPrefixes)) {
return $matches[2];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@ public function testExtract($property, array $type = null, $shortDescription, $l
$this->assertSame($longDescription, $this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
}

/**
* @dataProvider typesWithCustomPrefixesProvider
*/
public function testExtractTypesWithCustomPrefixes($property, array $type = null)
{
$customExtractor = new PhpDocExtractor(null, array('add', 'remove'), array('is', 'can'));

$this->assertEquals($type, $customExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
}

/**
* @dataProvider typesWithNoPrefixesProvider
*/
public function testExtractTypesWithNoPrefixes($property, array $type = null)
{
$noPrefixExtractor = new PhpDocExtractor(null, array(), array(), array());

$this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
}

public function typesProvider()
{
return array(
Expand Down Expand Up @@ -75,6 +95,76 @@ public function typesProvider()
);
}

public function typesWithCustomPrefixesProvider()
{
return array(
array('foo', null, 'Short description.', 'Long description.'),
array('bar', array(new Type(Type::BUILTIN_TYPE_STRING)), 'This is bar', null),
array('baz', array(new Type(Type::BUILTIN_TYPE_INT)), 'Should be used.', null),
array('foo2', array(new Type(Type::BUILTIN_TYPE_FLOAT)), null, null),
array('foo3', array(new Type(Type::BUILTIN_TYPE_CALLABLE)), null, null),
array('foo4', array(new Type(Type::BUILTIN_TYPE_NULL)), null, null),
array('foo5', null, null, null),
array(
'files',
array(
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
new Type(Type::BUILTIN_TYPE_RESOURCE),
),
null,
null,
),
array('bal', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')), null, null),
array('parent', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), null, null),
array('collection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null),
array('a', null, 'A.', null),
array('b', null, 'B.', null),
array('c', array(new Type(Type::BUILTIN_TYPE_BOOL, true)), null, null),
array('d', array(new Type(Type::BUILTIN_TYPE_BOOL)), null, null),
array('e', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))), null, null),
array('f', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null),
array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null),
array('donotexist', null, null, null),
array('staticGetter', null, null, null),
array('staticSetter', null, null, null),
);
}

public function typesWithNoPrefixesProvider()
{
return array(
array('foo', null, 'Short description.', 'Long description.'),
array('bar', array(new Type(Type::BUILTIN_TYPE_STRING)), 'This is bar', null),
array('baz', array(new Type(Type::BUILTIN_TYPE_INT)), 'Should be used.', null),
array('foo2', array(new Type(Type::BUILTIN_TYPE_FLOAT)), null, null),
array('foo3', array(new Type(Type::BUILTIN_TYPE_CALLABLE)), null, null),
array('foo4', array(new Type(Type::BUILTIN_TYPE_NULL)), null, null),
array('foo5', null, null, null),
array(
'files',
array(
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
new Type(Type::BUILTIN_TYPE_RESOURCE),
),
null,
null,
),
array('bal', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')), null, null),
array('parent', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), null, null),
array('collection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null),
array('a', null, 'A.', null),
array('b', null, 'B.', null),
array('c', null, null, null),
array('d', null, null, null),
array('e', null, null, null),
array('f', null, null, null),
array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null),
array('donotexist', null, null, null),
array('staticGetter', null, null, null),
array('staticSetter', null, null, null),
);
}

public function testReturnNullOnEmptyDocBlock()
{
$this->assertNull($this->extractor->getShortDescription(EmptyDocBlock::class, 'foo'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,56 @@ public function testGetProperties()
);
}

public function testGetPropertiesWithCustomPrefixes()
{
$customExtractor = new ReflectionExtractor(array('add', 'remove'), array('is', 'can'));

$this->assertSame(
array(
'bal',
'parent',
'collection',
'B',
'Guid',
'g',
'foo',
'foo2',
'foo3',
'foo4',
'foo5',
'files',
'c',
'd',
'e',
'f',
),
$customExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
);
}

public function testGetPropertiesWithNoPrefixes()
{
$noPrefixExtractor = new ReflectionExtractor(array(), array(), array());

$this->assertSame(
array(
'bal',
'parent',
'collection',
'B',
'Guid',
'g',
'foo',
'foo2',
'foo3',
'foo4',
'foo5',
'files',
),
$noPrefixExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
);
}

/**
* @dataProvider typesProvider
*/
Expand Down

0 comments on commit 384b34b

Please sign in to comment.