[PropertyInfo] Added support for extracting type from constructor

This commit is contained in:
Grégoire Pineau 2017-12-26 11:56:19 +01:00
parent b0facfec01
commit adcb25ec01
4 changed files with 138 additions and 1 deletions

View File

@ -44,17 +44,19 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
private $mutatorPrefixes;
private $accessorPrefixes;
private $arrayMutatorPrefixes;
private $enableConstructorExtraction;
/**
* @param string[]|null $mutatorPrefixes
* @param string[]|null $accessorPrefixes
* @param string[]|null $arrayMutatorPrefixes
*/
public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null)
public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null, bool $enableConstructorExtraction = true)
{
$this->mutatorPrefixes = null !== $mutatorPrefixes ? $mutatorPrefixes : self::$defaultMutatorPrefixes;
$this->accessorPrefixes = null !== $accessorPrefixes ? $accessorPrefixes : self::$defaultAccessorPrefixes;
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : self::$defaultArrayMutatorPrefixes;
$this->enableConstructorExtraction = $enableConstructorExtraction;
}
/**
@ -107,6 +109,13 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
if ($fromAccessor = $this->extractFromAccessor($class, $property)) {
return $fromAccessor;
}
if (
$context['enable_constructor_extraction'] ?? $this->enableConstructorExtraction &&
$fromConstructor = $this->extractFromConstructor($class, $property)
) {
return $fromConstructor;
}
}
/**
@ -185,6 +194,40 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
return null;
}
/**
* Tries to extract type information from constructor.
*
* @return Type[]|null
*/
private function extractFromConstructor(string $class, string $property): ?array
{
try {
$reflectionClass = new \ReflectionClass($class);
} catch (\ReflectionException $e) {
return null;
}
$constructor = $reflectionClass->getConstructor();
if (!$constructor) {
return null;
}
foreach ($constructor->getParameters() as $parameter) {
if ($property !== $parameter->name) {
continue;
}
return array($this->extractFromReflectionType($parameter->getType()));
}
if ($parentClass = $reflectionClass->getParentClass()) {
return $this->extractFromConstructor($parentClass->getName(), $property);
}
return null;
}
private function extractFromReflectionType(\ReflectionType $reflectionType): Type
{
$phpTypeOrClass = $reflectionType->getName();

View File

@ -114,6 +114,24 @@ class ReflectionExtractorTest extends TestCase
);
}
public function testGetPropertiesPhp71()
{
$noPrefixExtractor = new ReflectionExtractor();
$this->assertSame(
array(
'string',
'stringOrNull',
'foo',
'buz',
'bar',
'baz',
'intWithAccessor',
),
$noPrefixExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy')
);
}
/**
* @dataProvider typesProvider
*/
@ -171,9 +189,22 @@ class ReflectionExtractorTest extends TestCase
array('bar', array(new Type(Type::BUILTIN_TYPE_INT, true))),
array('baz', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))),
array('donotexist', null),
array('string', array(new Type(Type::BUILTIN_TYPE_STRING, false))),
array('stringOrNull', array(new Type(Type::BUILTIN_TYPE_STRING, true))),
array('intPrivate', array(new Type(Type::BUILTIN_TYPE_INT, false))),
array('intWithAccessor', array(new Type(Type::BUILTIN_TYPE_INT, false))),
);
}
public function testExtractPhp71TypeWithParentConstructor()
{
$property = 'string';
$type = array(new Type(Type::BUILTIN_TYPE_STRING, false));
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild', $property, array()));
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild2', $property, array()));
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild3', $property, array()));
}
/**
* @dataProvider getReadableProperties
*/

View File

@ -16,6 +16,22 @@ namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
*/
class Php71Dummy
{
public $string;
public $stringOrNull;
private $intPrivate;
private $intWithAccessor;
public function __construct(string $string, ?string $stringOrNull, int $intPrivate, int $intWithAccessor)
{
$this->string = $string;
$this->stringOrNull = $stringOrNull;
$this->intPrivate = $intPrivate;
$this->intWithAccessor = $intWithAccessor;
}
public function getFoo(): ?array
{
}
@ -31,4 +47,9 @@ class Php71Dummy
public function addBaz(string $baz)
{
}
public function getIntWithAccessor()
{
return $this->intWithAccessor;
}
}

View File

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
class Php71DummyParent
{
public $string;
public function __construct(string $string)
{
$this->string = $string;
}
}
class Php71DummyChild extends Php71DummyParent
{
public function __construct(string $string)
{
parent::__construct($string);
}
}
class Php71DummyChild2 extends Php71DummyParent
{
}
class Php71DummyChild3 extends Php71DummyParent
{
public function __construct()
{
parent::__construct('hello');
}
}