mirror of
https://github.com/rectorphp/rector.git
synced 2025-02-25 12:14:02 +01:00
Add support for stringy calls in CallReflectionResolver (#2663)
Add support for stringy calls in CallReflectionResolver
This commit is contained in:
commit
54c92ebfec
@ -15,7 +15,6 @@ services:
|
||||
- '../src/DependencyInjection/Loader/*'
|
||||
- '../src/HttpKernel/*'
|
||||
- '../src/ValueObject/*'
|
||||
- '../src/PHPStan/Reflection/Php/*'
|
||||
|
||||
# extra services
|
||||
Rector\Symfony\Rector\Form\Helper\FormTypeStringToTypeProvider: null
|
||||
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Php70\Tests\Rector\FuncCall\NonVariableToVariableOnFunctionCallRector\Fixture;
|
||||
|
||||
function anonymousClass()
|
||||
{
|
||||
$anonymousClass = new class {
|
||||
public function bar(&$baz) {}
|
||||
};
|
||||
$anonymousClass->bar(baz());
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Php70\Tests\Rector\FuncCall\NonVariableToVariableOnFunctionCallRector\Fixture;
|
||||
|
||||
function anonymousClass()
|
||||
{
|
||||
$anonymousClass = new class {
|
||||
public function bar(&$baz) {}
|
||||
};
|
||||
$baz = baz();
|
||||
$anonymousClass->bar($baz);
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Php70\Tests\Rector\FuncCall\NonVariableToVariableOnFunctionCallRector\Fixture;
|
||||
|
||||
function anonymousFunction()
|
||||
{
|
||||
$anonymousFunction = function (&$bar) {};
|
||||
$anonymousFunction(bar());
|
||||
$staticAnonymousFunction = static function (&$bar) {};
|
||||
$staticAnonymousFunction(bar());
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Php70\Tests\Rector\FuncCall\NonVariableToVariableOnFunctionCallRector\Fixture;
|
||||
|
||||
function anonymousFunction()
|
||||
{
|
||||
$anonymousFunction = function (&$bar) {};
|
||||
$bar = bar();
|
||||
$anonymousFunction($bar);
|
||||
$staticAnonymousFunction = static function (&$bar) {};
|
||||
$bar = bar();
|
||||
$staticAnonymousFunction($bar);
|
||||
}
|
||||
|
||||
?>
|
@ -13,11 +13,6 @@ function funcCalls()
|
||||
allByRef(bar(), baz());
|
||||
allByRef(1, 2);
|
||||
|
||||
$anonymousFunction = function (&$bar) {};
|
||||
$staticAnonymousFunction = static function (&$bar) {};
|
||||
$anonymousFunction(bar());
|
||||
$staticAnonymousFunction(bar());
|
||||
|
||||
return byRef(1, bar());
|
||||
}
|
||||
|
||||
@ -43,13 +38,6 @@ function funcCalls()
|
||||
$tmp = 1;
|
||||
$tmp2 = 2;
|
||||
allByRef($tmp, $tmp2);
|
||||
|
||||
$anonymousFunction = function (&$bar) {};
|
||||
$staticAnonymousFunction = static function (&$bar) {};
|
||||
$bar = bar();
|
||||
$anonymousFunction($bar);
|
||||
$bar = bar();
|
||||
$staticAnonymousFunction($bar);
|
||||
$bar = bar();
|
||||
|
||||
return byRef(1, $bar);
|
||||
|
@ -25,11 +25,6 @@ function methodCalls()
|
||||
$aClass = new AClass();
|
||||
$aClass->baz(baz());
|
||||
$aClass->child()->bar(bar());
|
||||
|
||||
$anonymousClass = new class {
|
||||
public function bar(&$baz) {}
|
||||
};
|
||||
$anonymousClass->bar(baz());
|
||||
}
|
||||
|
||||
?>
|
||||
@ -64,12 +59,6 @@ function methodCalls()
|
||||
$aClass->baz($baz);
|
||||
$bar = bar();
|
||||
$aClass->child()->bar($bar);
|
||||
|
||||
$anonymousClass = new class {
|
||||
public function bar(&$baz) {}
|
||||
};
|
||||
$baz = baz();
|
||||
$anonymousClass->bar($baz);
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Php70\Tests\Rector\FuncCall\NonVariableToVariableOnFunctionCallRector\Fixture;
|
||||
|
||||
class MyClass
|
||||
{
|
||||
public static function staticMethod(&$bar) {}
|
||||
}
|
||||
|
||||
function stringyCalls()
|
||||
{
|
||||
$functionName = 'reset';
|
||||
$functionName(bar());
|
||||
|
||||
$methodName = MyClass::class.'::staticMethod';
|
||||
$methodName(bar());
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Php70\Tests\Rector\FuncCall\NonVariableToVariableOnFunctionCallRector\Fixture;
|
||||
|
||||
class MyClass
|
||||
{
|
||||
public static function staticMethod(&$bar) {}
|
||||
}
|
||||
|
||||
function stringyCalls()
|
||||
{
|
||||
$functionName = 'reset';
|
||||
$bar = bar();
|
||||
$functionName($bar);
|
||||
|
||||
$methodName = MyClass::class.'::staticMethod';
|
||||
$bar = bar();
|
||||
$methodName($bar);
|
||||
}
|
||||
|
||||
?>
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStan\Reflection;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
@ -13,17 +14,27 @@ use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Broker\FunctionNotFoundException;
|
||||
use PHPStan\Reflection\FunctionReflection;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Reflection\Native\NativeFunctionReflection;
|
||||
use PHPStan\Reflection\ParametersAcceptor;
|
||||
use PHPStan\Reflection\ParametersAcceptorSelector;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ClosureType;
|
||||
use PHPStan\TrinaryLogic;
|
||||
use PHPStan\Type\Constant\ConstantStringType;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use Rector\PHPStan\Reflection\Php\ClosureInvokeMethodReflection;
|
||||
|
||||
final class CallReflectionResolver
|
||||
{
|
||||
/**
|
||||
* Took from https://github.com/phpstan/phpstan-src/blob/8376548f76e2c845ae047e3010e873015b796818/src/Type/Constant/ConstantStringType.php#L158
|
||||
*
|
||||
* @see https://regex101.com/r/IE6lcM/4
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private const STATIC_METHOD_REGEXP = '#^([a-zA-Z_\\x7f-\\xff\\\\][a-zA-Z0-9_\\x7f-\\xff\\\\]*)::([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)\\z#';
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
@ -83,11 +94,17 @@ final class CallReflectionResolver
|
||||
}
|
||||
|
||||
$type = $scope->getType($funcCall->name);
|
||||
if (! $type instanceof ClosureType) {
|
||||
return null;
|
||||
|
||||
if (! $type instanceof ConstantStringType) {
|
||||
return new NativeFunctionReflection(
|
||||
'{closure}',
|
||||
$type->getCallableParametersAcceptors($scope),
|
||||
null,
|
||||
TrinaryLogic::createMaybe()
|
||||
);
|
||||
}
|
||||
|
||||
return new ClosureInvokeMethodReflection($type->getMethod('__invoke', $scope), $type);
|
||||
return $this->resolveConstantString($type, $scope);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,4 +159,35 @@ final class CallReflectionResolver
|
||||
|
||||
return $parametersAcceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FunctionReflection|MethodReflection|null
|
||||
*/
|
||||
private function resolveConstantString(ConstantStringType $constantStringType, Scope $scope)
|
||||
{
|
||||
$value = $constantStringType->getValue();
|
||||
|
||||
// 'my_function'
|
||||
$functionName = new Name($value);
|
||||
if ($this->reflectionProvider->hasFunction($functionName, null)) {
|
||||
return $this->reflectionProvider->getFunction($functionName, null);
|
||||
}
|
||||
|
||||
// 'MyClass::myStaticFunction'
|
||||
$matches = Strings::match($value, self::STATIC_METHOD_REGEXP);
|
||||
if ($matches === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->reflectionProvider->hasClass($matches[1])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($matches[1]);
|
||||
if (! $classReflection->hasMethod($matches[2])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $classReflection->getMethod($matches[2], $scope);
|
||||
}
|
||||
}
|
||||
|
@ -1,110 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStan\Reflection\Php;
|
||||
|
||||
use PHPStan\Reflection\ClassMemberReflection;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\FunctionVariant;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\TrinaryLogic;
|
||||
use PHPStan\Type\ClosureType;
|
||||
use PHPStan\Type\Type;
|
||||
|
||||
final class ClosureInvokeMethodReflection implements MethodReflection
|
||||
{
|
||||
/**
|
||||
* @var MethodReflection
|
||||
*/
|
||||
private $nativeMethodReflection;
|
||||
|
||||
/**
|
||||
* @var ClosureType
|
||||
*/
|
||||
private $closureType;
|
||||
|
||||
public function __construct(MethodReflection $nativeMethodReflection, ClosureType $closureType)
|
||||
{
|
||||
$this->nativeMethodReflection = $nativeMethodReflection;
|
||||
$this->closureType = $closureType;
|
||||
}
|
||||
|
||||
public function getDeclaringClass(): ClassReflection
|
||||
{
|
||||
return $this->nativeMethodReflection->getDeclaringClass();
|
||||
}
|
||||
|
||||
public function isStatic(): bool
|
||||
{
|
||||
return $this->nativeMethodReflection->isStatic();
|
||||
}
|
||||
|
||||
public function isPrivate(): bool
|
||||
{
|
||||
return $this->nativeMethodReflection->isPrivate();
|
||||
}
|
||||
|
||||
public function isPublic(): bool
|
||||
{
|
||||
return $this->nativeMethodReflection->isPublic();
|
||||
}
|
||||
|
||||
public function getDocComment(): ?string
|
||||
{
|
||||
return $this->nativeMethodReflection->getDocComment();
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->nativeMethodReflection->getName();
|
||||
}
|
||||
|
||||
public function getPrototype(): ClassMemberReflection
|
||||
{
|
||||
return $this->nativeMethodReflection->getPrototype();
|
||||
}
|
||||
|
||||
public function getVariants(): array
|
||||
{
|
||||
return [
|
||||
new FunctionVariant(
|
||||
$this->closureType->getTemplateTypeMap(),
|
||||
$this->closureType->getResolvedTemplateTypeMap(),
|
||||
$this->closureType->getParameters(),
|
||||
$this->closureType->isVariadic(),
|
||||
$this->closureType->getReturnType()
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
public function isDeprecated(): TrinaryLogic
|
||||
{
|
||||
return $this->nativeMethodReflection->isDeprecated();
|
||||
}
|
||||
|
||||
public function getDeprecatedDescription(): ?string
|
||||
{
|
||||
return $this->nativeMethodReflection->getDeprecatedDescription();
|
||||
}
|
||||
|
||||
public function isFinal(): TrinaryLogic
|
||||
{
|
||||
return $this->nativeMethodReflection->isFinal();
|
||||
}
|
||||
|
||||
public function isInternal(): TrinaryLogic
|
||||
{
|
||||
return $this->nativeMethodReflection->isInternal();
|
||||
}
|
||||
|
||||
public function getThrowType(): ?Type
|
||||
{
|
||||
return $this->nativeMethodReflection->getThrowType();
|
||||
}
|
||||
|
||||
public function hasSideEffects(): TrinaryLogic
|
||||
{
|
||||
return $this->nativeMethodReflection->hasSideEffects();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user