bug #20154 [PropertyInfo] Fix edge cases in ReflectionExtractor (nicolas-grekas)
This PR was merged into the 2.8 branch.
Discussion
----------
[PropertyInfo] Fix edge cases in ReflectionExtractor
| Q | A
| ------------- | ---
| Branch? | 2.8
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
This should make ReflectionExtractor a bit more robust.
ping @dunglas (~~don't miss the changed test case~~).
Commits
-------
bffdfad
[PropertyInfo] Fix edge cases in ReflectionExtractor
This commit is contained in:
commit
781c923c93
@ -44,6 +44,13 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
|||||||
*/
|
*/
|
||||||
public static $arrayMutatorPrefixes = array('add', 'remove');
|
public static $arrayMutatorPrefixes = array('add', 'remove');
|
||||||
|
|
||||||
|
private $supportsParameterType;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->supportsParameterType = method_exists('ReflectionParameter', 'getType');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
@ -134,68 +141,33 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
|||||||
$reflectionParameters = $reflectionMethod->getParameters();
|
$reflectionParameters = $reflectionMethod->getParameters();
|
||||||
$reflectionParameter = $reflectionParameters[0];
|
$reflectionParameter = $reflectionParameters[0];
|
||||||
|
|
||||||
$arrayMutator = in_array($prefix, self::$arrayMutatorPrefixes);
|
if ($this->supportsParameterType) {
|
||||||
|
if (!$reflectionType = $reflectionParameter->getType()) {
|
||||||
if (method_exists($reflectionParameter, 'getType') && $reflectionType = $reflectionParameter->getType()) {
|
return;
|
||||||
$fromReflectionType = $this->extractFromReflectionType($reflectionType);
|
|
||||||
|
|
||||||
if (!$arrayMutator) {
|
|
||||||
return array($fromReflectionType);
|
|
||||||
}
|
}
|
||||||
|
$type = $this->extractFromReflectionType($reflectionType);
|
||||||
|
|
||||||
$phpType = Type::BUILTIN_TYPE_ARRAY;
|
// HHVM reports variadics with "array" but not builtin type hints
|
||||||
$collectionKeyType = new Type(Type::BUILTIN_TYPE_INT);
|
if (!$reflectionType->isBuiltin() && Type::BUILTIN_TYPE_ARRAY === $type->getBuiltinType()) {
|
||||||
$collectionValueType = $fromReflectionType;
|
return;
|
||||||
}
|
}
|
||||||
|
} elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $reflectionParameter, $info)) {
|
||||||
if ($reflectionParameter->isArray()) {
|
if (Type::BUILTIN_TYPE_ARRAY === $info[1]) {
|
||||||
$phpType = Type::BUILTIN_TYPE_ARRAY;
|
$type = new Type(Type::BUILTIN_TYPE_ARRAY, $reflectionParameter->allowsNull(), null, true);
|
||||||
$collection = true;
|
} elseif (Type::BUILTIN_TYPE_CALLABLE === $info[1]) {
|
||||||
}
|
$type = new Type(Type::BUILTIN_TYPE_CALLABLE, $reflectionParameter->allowsNull());
|
||||||
|
|
||||||
if ($arrayMutator) {
|
|
||||||
$collection = true;
|
|
||||||
$nullable = false;
|
|
||||||
$collectionNullable = $reflectionParameter->allowsNull();
|
|
||||||
} else {
|
|
||||||
$nullable = $reflectionParameter->allowsNull();
|
|
||||||
$collectionNullable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($collection)) {
|
|
||||||
$collection = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method_exists($reflectionParameter, 'isCallable') && $reflectionParameter->isCallable()) {
|
|
||||||
$phpType = Type::BUILTIN_TYPE_CALLABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($typeHint = $reflectionParameter->getClass()) {
|
|
||||||
if ($collection) {
|
|
||||||
$phpType = Type::BUILTIN_TYPE_ARRAY;
|
|
||||||
$collectionKeyType = new Type(Type::BUILTIN_TYPE_INT);
|
|
||||||
$collectionValueType = new Type(Type::BUILTIN_TYPE_OBJECT, $collectionNullable, $typeHint->name);
|
|
||||||
} else {
|
} else {
|
||||||
$phpType = Type::BUILTIN_TYPE_OBJECT;
|
$type = new Type(Type::BUILTIN_TYPE_OBJECT, $reflectionParameter->allowsNull(), $info[1]);
|
||||||
$typeClass = $typeHint->name;
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
// Nothing useful extracted
|
|
||||||
if (!isset($phpType)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array(
|
if (in_array($prefix, self::$arrayMutatorPrefixes)) {
|
||||||
new Type(
|
$type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type);
|
||||||
$phpType,
|
}
|
||||||
$nullable,
|
|
||||||
isset($typeClass) ? $typeClass : null,
|
return array($type);
|
||||||
$collection,
|
|
||||||
isset($collectionKeyType) ? $collectionKeyType : null,
|
|
||||||
isset($collectionValueType) ? $collectionValueType : null
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -213,7 +185,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method_exists($reflectionMethod, 'getReturnType') && $reflectionType = $reflectionMethod->getReturnType()) {
|
if ($this->supportsParameterType && $reflectionType = $reflectionMethod->getReturnType()) {
|
||||||
return array($this->extractFromReflectionType($reflectionType));
|
return array($this->extractFromReflectionType($reflectionType));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,15 +203,15 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
|
|||||||
*/
|
*/
|
||||||
private function extractFromReflectionType(\ReflectionType $reflectionType)
|
private function extractFromReflectionType(\ReflectionType $reflectionType)
|
||||||
{
|
{
|
||||||
$phpTypeOrClass = method_exists($reflectionType, 'getName') ? $reflectionType->getName() : (string) $reflectionType;
|
$phpTypeOrClass = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : $reflectionType->__toString();
|
||||||
$nullable = $reflectionType->allowsNull();
|
$nullable = $reflectionType->allowsNull();
|
||||||
|
|
||||||
if ($reflectionType->isBuiltin()) {
|
if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) {
|
||||||
if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) {
|
$type = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true);
|
||||||
$type = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true);
|
} elseif ('void' === $phpTypeOrClass) {
|
||||||
} else {
|
$type = new Type(Type::BUILTIN_TYPE_NULL, $nullable);
|
||||||
$type = new Type($phpTypeOrClass, $nullable);
|
} elseif ($reflectionType->isBuiltin()) {
|
||||||
}
|
$type = new Type($phpTypeOrClass, $nullable);
|
||||||
} else {
|
} else {
|
||||||
$type = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $phpTypeOrClass);
|
$type = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $phpTypeOrClass);
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,7 @@ class ReflectionExtractorTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
array('foo', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true))),
|
array('foo', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true))),
|
||||||
|
array('buz', array(new Type(Type::BUILTIN_TYPE_NULL))),
|
||||||
array('bar', array(new Type(Type::BUILTIN_TYPE_INT, true))),
|
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('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('donotexist', null),
|
||||||
|
@ -20,6 +20,10 @@ class Php71Dummy
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getBuz(): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function setBar(?int $bar)
|
public function setBar(?int $bar)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user