Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}

?>
Original file line number Diff line number Diff line change
@@ -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();
Comment thread
Lctrs marked this conversation as resolved.
$anonymousFunction($bar);
$staticAnonymousFunction = static function (&$bar) {};
$bar = bar();
$staticAnonymousFunction($bar);
}

?>
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

?>
Expand Down Expand Up @@ -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);
}

?>
Original file line number Diff line number Diff line change
@@ -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);
}

?>
58 changes: 53 additions & 5 deletions src/PHPStan/Reflection/CallReflectionResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Rector\PHPStan\Reflection;

use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
Expand All @@ -13,17 +14,27 @@
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
*/
Expand Down Expand Up @@ -83,11 +94,17 @@ public function resolveFunctionCall(FuncCall $funcCall)
}

$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);
}

/**
Expand Down Expand Up @@ -142,4 +159,35 @@ public function resolveParametersAcceptor($reflection, Node $node): ?ParametersA

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);
}
}
110 changes: 0 additions & 110 deletions src/PHPStan/Reflection/Php/ClosureInvokeMethodReflection.php

This file was deleted.