Skip to content

Commit adcb25e

Browse files
committed
[PropertyInfo] Added support for extracting type from constructor
1 parent b0facfe commit adcb25e

File tree

4 files changed

+138
-1
lines changed

4 files changed

+138
-1
lines changed

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

+44-1
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,19 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
4444
private $mutatorPrefixes;
4545
private $accessorPrefixes;
4646
private $arrayMutatorPrefixes;
47+
private $enableConstructorExtraction;
4748

4849
/**
4950
* @param string[]|null $mutatorPrefixes
5051
* @param string[]|null $accessorPrefixes
5152
* @param string[]|null $arrayMutatorPrefixes
5253
*/
53-
public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null)
54+
public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null, bool $enableConstructorExtraction = true)
5455
{
5556
$this->mutatorPrefixes = null !== $mutatorPrefixes ? $mutatorPrefixes : self::$defaultMutatorPrefixes;
5657
$this->accessorPrefixes = null !== $accessorPrefixes ? $accessorPrefixes : self::$defaultAccessorPrefixes;
5758
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : self::$defaultArrayMutatorPrefixes;
59+
$this->enableConstructorExtraction = $enableConstructorExtraction;
5860
}
5961

6062
/**
@@ -107,6 +109,13 @@ public function getTypes($class, $property, array $context = array())
107109
if ($fromAccessor = $this->extractFromAccessor($class, $property)) {
108110
return $fromAccessor;
109111
}
112+
113+
if (
114+
$context['enable_constructor_extraction'] ?? $this->enableConstructorExtraction &&
115+
$fromConstructor = $this->extractFromConstructor($class, $property)
116+
) {
117+
return $fromConstructor;
118+
}
110119
}
111120

112121
/**
@@ -185,6 +194,40 @@ private function extractFromAccessor(string $class, string $property): ?array
185194
return null;
186195
}
187196

197+
/**
198+
* Tries to extract type information from constructor.
199+
*
200+
* @return Type[]|null
201+
*/
202+
private function extractFromConstructor(string $class, string $property): ?array
203+
{
204+
try {
205+
$reflectionClass = new \ReflectionClass($class);
206+
} catch (\ReflectionException $e) {
207+
return null;
208+
}
209+
210+
$constructor = $reflectionClass->getConstructor();
211+
212+
if (!$constructor) {
213+
return null;
214+
}
215+
216+
foreach ($constructor->getParameters() as $parameter) {
217+
if ($property !== $parameter->name) {
218+
continue;
219+
}
220+
221+
return array($this->extractFromReflectionType($parameter->getType()));
222+
}
223+
224+
if ($parentClass = $reflectionClass->getParentClass()) {
225+
return $this->extractFromConstructor($parentClass->getName(), $property);
226+
}
227+
228+
return null;
229+
}
230+
188231
private function extractFromReflectionType(\ReflectionType $reflectionType): Type
189232
{
190233
$phpTypeOrClass = $reflectionType->getName();

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

+31
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,24 @@ public function testGetPropertiesWithNoPrefixes()
114114
);
115115
}
116116

117+
public function testGetPropertiesPhp71()
118+
{
119+
$noPrefixExtractor = new ReflectionExtractor();
120+
121+
$this->assertSame(
122+
array(
123+
'string',
124+
'stringOrNull',
125+
'foo',
126+
'buz',
127+
'bar',
128+
'baz',
129+
'intWithAccessor',
130+
),
131+
$noPrefixExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy')
132+
);
133+
}
134+
117135
/**
118136
* @dataProvider typesProvider
119137
*/
@@ -171,9 +189,22 @@ public function php71TypesProvider()
171189
array('bar', array(new Type(Type::BUILTIN_TYPE_INT, true))),
172190
array('baz', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))),
173191
array('donotexist', null),
192+
array('string', array(new Type(Type::BUILTIN_TYPE_STRING, false))),
193+
array('stringOrNull', array(new Type(Type::BUILTIN_TYPE_STRING, true))),
194+
array('intPrivate', array(new Type(Type::BUILTIN_TYPE_INT, false))),
195+
array('intWithAccessor', array(new Type(Type::BUILTIN_TYPE_INT, false))),
174196
);
175197
}
176198

199+
public function testExtractPhp71TypeWithParentConstructor()
200+
{
201+
$property = 'string';
202+
$type = array(new Type(Type::BUILTIN_TYPE_STRING, false));
203+
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild', $property, array()));
204+
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild2', $property, array()));
205+
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyChild3', $property, array()));
206+
}
207+
177208
/**
178209
* @dataProvider getReadableProperties
179210
*/

src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php71Dummy.php

+21
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,22 @@
1616
*/
1717
class Php71Dummy
1818
{
19+
public $string;
20+
21+
public $stringOrNull;
22+
23+
private $intPrivate;
24+
25+
private $intWithAccessor;
26+
27+
public function __construct(string $string, ?string $stringOrNull, int $intPrivate, int $intWithAccessor)
28+
{
29+
$this->string = $string;
30+
$this->stringOrNull = $stringOrNull;
31+
$this->intPrivate = $intPrivate;
32+
$this->intWithAccessor = $intWithAccessor;
33+
}
34+
1935
public function getFoo(): ?array
2036
{
2137
}
@@ -31,4 +47,9 @@ public function setBar(?int $bar)
3147
public function addBaz(string $baz)
3248
{
3349
}
50+
51+
public function getIntWithAccessor()
52+
{
53+
return $this->intWithAccessor;
54+
}
3455
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
class Php71DummyParent
15+
{
16+
public $string;
17+
18+
public function __construct(string $string)
19+
{
20+
$this->string = $string;
21+
}
22+
}
23+
24+
class Php71DummyChild extends Php71DummyParent
25+
{
26+
public function __construct(string $string)
27+
{
28+
parent::__construct($string);
29+
}
30+
}
31+
32+
class Php71DummyChild2 extends Php71DummyParent
33+
{
34+
}
35+
36+
class Php71DummyChild3 extends Php71DummyParent
37+
{
38+
public function __construct()
39+
{
40+
parent::__construct('hello');
41+
}
42+
}

0 commit comments

Comments
 (0)