Skip to content

Commit

Permalink
feature #30706 [PropertyInfo] Add possibility to extract private and …
Browse files Browse the repository at this point in the history
…protected properties in reflection extractor (joelwurtz)

This PR was squashed before being merged into the 4.3-dev branch (closes #30706).

Discussion
----------

[PropertyInfo] Add possibility to extract private and protected properties in reflection extractor

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #30248
| License       | MIT
| Doc PR        | TODO

This PR add the possibility to extract private and protected properties from a class by passing a new argument to the `ReflectionExtractor`

This new argument consist of flag that filters properties, so someone will also be able to use the `ReflectionExtractor` only for private property

```php
new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PRIVATE | ReflectionExtractor::ALLOW_PROTECTED)
```

Flags method was prefered over a list of bool to avoid too many parameters and also be close to the reflection API of PHP

Commits
-------

05e487f [PropertyInfo] Add possibility to extract private and protected properties in reflection extractor
  • Loading branch information
fabpot committed Mar 27, 2019
2 parents bc18e39 + 05e487f commit 8952c02
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 7 deletions.
Expand Up @@ -42,6 +42,10 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
*/
public static $defaultArrayMutatorPrefixes = ['add', 'remove'];

public const ALLOW_PRIVATE = 1;
public const ALLOW_PROTECTED = 2;
public const ALLOW_PUBLIC = 4;

private const MAP_TYPES = [
'integer' => Type::BUILTIN_TYPE_INT,
'boolean' => Type::BUILTIN_TYPE_BOOL,
Expand All @@ -52,18 +56,20 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
private $accessorPrefixes;
private $arrayMutatorPrefixes;
private $enableConstructorExtraction;
private $accessFlags;

/**
* @param string[]|null $mutatorPrefixes
* @param string[]|null $accessorPrefixes
* @param string[]|null $arrayMutatorPrefixes
*/
public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null, bool $enableConstructorExtraction = true)
public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null, bool $enableConstructorExtraction = true, int $accessFlags = self::ALLOW_PUBLIC)
{
$this->mutatorPrefixes = null !== $mutatorPrefixes ? $mutatorPrefixes : self::$defaultMutatorPrefixes;
$this->accessorPrefixes = null !== $accessorPrefixes ? $accessorPrefixes : self::$defaultAccessorPrefixes;
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : self::$defaultArrayMutatorPrefixes;
$this->enableConstructorExtraction = $enableConstructorExtraction;
$this->accessFlags = $accessFlags;
}

/**
Expand All @@ -77,16 +83,34 @@ public function getProperties($class, array $context = [])
return;
}

$propertyFlags = 0;
$methodFlags = 0;

if ($this->accessFlags & self::ALLOW_PUBLIC) {
$propertyFlags = $propertyFlags | \ReflectionProperty::IS_PUBLIC;
$methodFlags = $methodFlags | \ReflectionMethod::IS_PUBLIC;
}

if ($this->accessFlags & self::ALLOW_PRIVATE) {
$propertyFlags = $propertyFlags | \ReflectionProperty::IS_PRIVATE;
$methodFlags = $methodFlags | \ReflectionMethod::IS_PRIVATE;
}

if ($this->accessFlags & self::ALLOW_PROTECTED) {
$propertyFlags = $propertyFlags | \ReflectionProperty::IS_PROTECTED;
$methodFlags = $methodFlags | \ReflectionMethod::IS_PROTECTED;
}

$reflectionProperties = $reflectionClass->getProperties();

$properties = [];
foreach ($reflectionProperties as $reflectionProperty) {
if ($reflectionProperty->isPublic()) {
if ($reflectionProperty->getModifiers() & $propertyFlags) {
$properties[$reflectionProperty->name] = $reflectionProperty->name;
}
}

foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
foreach ($reflectionClass->getMethods($methodFlags) as $reflectionMethod) {
if ($reflectionMethod->isStatic()) {
continue;
}
Expand Down Expand Up @@ -134,7 +158,7 @@ public function getTypes($class, $property, array $context = [])
*/
public function isReadable($class, $property, array $context = [])
{
if ($this->isPublicProperty($class, $property)) {
if ($this->isAllowedProperty($class, $property)) {
return true;
}

Expand All @@ -148,7 +172,7 @@ public function isReadable($class, $property, array $context = [])
*/
public function isWritable($class, $property, array $context = [])
{
if ($this->isPublicProperty($class, $property)) {
if ($this->isAllowedProperty($class, $property)) {
return true;
}

Expand Down Expand Up @@ -317,12 +341,24 @@ private function resolveTypeName(string $name, \ReflectionMethod $reflectionMeth
return $name;
}

private function isPublicProperty(string $class, string $property): bool
private function isAllowedProperty(string $class, string $property): bool
{
try {
$reflectionProperty = new \ReflectionProperty($class, $property);

return $reflectionProperty->isPublic();
if ($this->accessFlags & self::ALLOW_PUBLIC && $reflectionProperty->isPublic()) {
return true;
}

if ($this->accessFlags & self::ALLOW_PROTECTED && $reflectionProperty->isProtected()) {
return true;
}

if ($this->accessFlags & self::ALLOW_PRIVATE && $reflectionProperty->isPrivate()) {
return true;
}

return false;
} catch (\ReflectionException $e) {
// Return false if the property doesn't exist
}
Expand Down
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\Tests\Fixtures\AdderRemoverDummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\DefaultValue;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\NotInstantiable;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyExtended2;
Expand Down Expand Up @@ -294,6 +295,27 @@ public function testSingularize()
$this->assertEquals(['analyses', 'feet'], $this->extractor->getProperties(AdderRemoverDummy::class));
}

public function testPrivatePropertyExtractor()
{
$privateExtractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PRIVATE | ReflectionExtractor::ALLOW_PROTECTED);
$properties = $privateExtractor->getProperties(Dummy::class);

$this->assertContains('bar', $properties);
$this->assertContains('baz', $properties);

$this->assertTrue($privateExtractor->isReadable(Dummy::class, 'bar'));
$this->assertTrue($privateExtractor->isReadable(Dummy::class, 'baz'));

$protectedExtractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PROTECTED);
$properties = $protectedExtractor->getProperties(Dummy::class);

$this->assertNotContains('bar', $properties);
$this->assertContains('baz', $properties);

$this->assertFalse($protectedExtractor->isReadable(Dummy::class, 'bar'));
$this->assertTrue($protectedExtractor->isReadable(Dummy::class, 'baz'));
}

/**
* @dataProvider getInitializableProperties
*/
Expand Down

0 comments on commit 8952c02

Please sign in to comment.