2020-10-14 19:14:55 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace Rector\DeadCode\Comparator;
|
|
|
|
|
|
|
|
use PhpParser\Node\Arg;
|
|
|
|
use PhpParser\Node\Expr\StaticCall;
|
|
|
|
use PhpParser\Node\Param;
|
|
|
|
use PhpParser\Node\Stmt\ClassMethod;
|
|
|
|
use PHPStan\Analyser\Scope;
|
2021-02-28 08:47:48 +01:00
|
|
|
use PHPStan\Reflection\ClassReflection;
|
2020-10-14 19:14:55 +02:00
|
|
|
use PHPStan\Reflection\MethodReflection;
|
|
|
|
use Rector\Core\Exception\ShouldNotHappenException;
|
2021-02-19 13:01:23 +01:00
|
|
|
use Rector\Core\PhpParser\Comparing\NodeComparator;
|
2020-10-14 19:14:55 +02:00
|
|
|
use Rector\DeadCode\Comparator\Parameter\ParameterDefaultsComparator;
|
|
|
|
use Rector\DeadCode\Comparator\Parameter\ParameterTypeComparator;
|
|
|
|
use Rector\NodeCollector\Reflection\MethodReflectionProvider;
|
|
|
|
use Rector\NodeNameResolver\NodeNameResolver;
|
|
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
|
|
|
|
|
|
|
final class CurrentAndParentClassMethodComparator
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var NodeNameResolver
|
|
|
|
*/
|
|
|
|
private $nodeNameResolver;
|
|
|
|
|
|
|
|
/**
|
2021-02-19 13:01:23 +01:00
|
|
|
* @var NodeComparator
|
2020-10-14 19:14:55 +02:00
|
|
|
*/
|
2021-02-19 13:01:23 +01:00
|
|
|
private $nodeComparator;
|
2020-10-14 19:14:55 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var MethodReflectionProvider
|
|
|
|
*/
|
|
|
|
private $methodReflectionProvider;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var ParameterDefaultsComparator
|
|
|
|
*/
|
|
|
|
private $parameterDefaultsComparator;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var ParameterTypeComparator
|
|
|
|
*/
|
|
|
|
private $parameterTypeComparator;
|
|
|
|
|
|
|
|
public function __construct(
|
|
|
|
NodeNameResolver $nodeNameResolver,
|
|
|
|
MethodReflectionProvider $methodReflectionProvider,
|
|
|
|
ParameterDefaultsComparator $parameterDefaultsComparator,
|
2021-02-19 13:01:23 +01:00
|
|
|
ParameterTypeComparator $parameterTypeComparator,
|
|
|
|
NodeComparator $nodeComparator
|
2020-10-14 19:14:55 +02:00
|
|
|
) {
|
|
|
|
$this->nodeNameResolver = $nodeNameResolver;
|
|
|
|
$this->methodReflectionProvider = $methodReflectionProvider;
|
|
|
|
$this->parameterDefaultsComparator = $parameterDefaultsComparator;
|
|
|
|
$this->parameterTypeComparator = $parameterTypeComparator;
|
2021-02-19 13:01:23 +01:00
|
|
|
$this->nodeComparator = $nodeComparator;
|
2020-10-14 19:14:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function isParentCallMatching(ClassMethod $classMethod, StaticCall $staticCall): bool
|
|
|
|
{
|
|
|
|
if (! $this->isSameMethodParentCall($classMethod, $staticCall)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! $this->areArgsAndParamsEqual($staticCall->args, $classMethod->params)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! $this->parameterTypeComparator->compareCurrentClassMethodAndParentStaticCall($classMethod, $staticCall)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ! $this->isParentClassMethodVisibilityOrDefaultOverride($classMethod, $staticCall);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function isSameMethodParentCall(ClassMethod $classMethod, StaticCall $staticCall): bool
|
|
|
|
{
|
|
|
|
if (! $this->nodeNameResolver->areNamesEqual($staticCall->name, $classMethod->name)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->nodeNameResolver->isName($staticCall->class, 'parent');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param Arg[] $parentStaticCallArgs
|
|
|
|
* @param Param[] $currentClassMethodParams
|
|
|
|
*/
|
|
|
|
private function areArgsAndParamsEqual(array $parentStaticCallArgs, array $currentClassMethodParams): bool
|
|
|
|
{
|
|
|
|
if (count($parentStaticCallArgs) !== count($currentClassMethodParams)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-10-29 15:59:39 +07:00
|
|
|
if ($parentStaticCallArgs === []) {
|
2020-10-14 19:14:55 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($parentStaticCallArgs as $key => $arg) {
|
|
|
|
if (! isset($currentClassMethodParams[$key])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this only compares variable name, but those can be differnt, so its kinda useless
|
|
|
|
$param = $currentClassMethodParams[$key];
|
2021-02-19 13:01:23 +01:00
|
|
|
if (! $this->nodeComparator->areNodesEqual($param->var, $arg->value)) {
|
2020-10-14 19:14:55 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function isParentClassMethodVisibilityOrDefaultOverride(
|
|
|
|
ClassMethod $classMethod,
|
|
|
|
StaticCall $staticCall
|
|
|
|
): bool {
|
2021-02-28 08:47:48 +01:00
|
|
|
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
|
|
|
if (! $scope instanceof Scope) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-10-14 19:14:55 +02:00
|
|
|
|
2021-02-28 08:47:48 +01:00
|
|
|
$classReflection = $scope->getClassReflection();
|
|
|
|
if (! $classReflection instanceof ClassReflection) {
|
|
|
|
return false;
|
2020-10-14 19:14:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$methodName = $this->nodeNameResolver->getName($staticCall->name);
|
2021-02-28 08:47:48 +01:00
|
|
|
if ($methodName === null) {
|
|
|
|
return false;
|
2021-02-21 16:32:45 +07:00
|
|
|
}
|
2021-02-28 08:47:48 +01:00
|
|
|
|
|
|
|
foreach ($classReflection->getParents() as $parentClassReflection) {
|
|
|
|
if (! $parentClassReflection->hasMethod($methodName)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$nativeParentClassReflection = $parentClassReflection->getNativeReflection();
|
|
|
|
$nativeParentClassMethodReflection = $nativeParentClassReflection->getMethod($methodName);
|
|
|
|
|
|
|
|
if (! $nativeParentClassMethodReflection->isProtected()) {
|
|
|
|
return $this->checkOverrideUsingReflection($classMethod, $parentClassReflection, $methodName);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! $nativeParentClassMethodReflection->isPublic()) {
|
|
|
|
return $this->checkOverrideUsingReflection($classMethod, $parentClassReflection, $methodName);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2021-02-21 16:32:45 +07:00
|
|
|
}
|
2021-02-28 08:47:48 +01:00
|
|
|
|
|
|
|
return false;
|
2020-10-14 19:14:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private function checkOverrideUsingReflection(
|
|
|
|
ClassMethod $classMethod,
|
2021-02-28 08:47:48 +01:00
|
|
|
ClassReflection $classReflection,
|
2020-10-14 19:14:55 +02:00
|
|
|
string $methodName
|
|
|
|
): bool {
|
|
|
|
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
|
|
|
if (! $scope instanceof Scope) {
|
|
|
|
throw new ShouldNotHappenException();
|
|
|
|
}
|
|
|
|
|
2021-02-28 08:47:48 +01:00
|
|
|
$parentMethodReflection = $classReflection->getMethod($methodName, $scope);
|
2020-10-14 19:14:55 +02:00
|
|
|
|
|
|
|
// 3rd party code
|
|
|
|
if ($parentMethodReflection !== null) {
|
|
|
|
if (! $parentMethodReflection->isPrivate() && ! $parentMethodReflection->isPublic() && $classMethod->isPublic()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($parentMethodReflection->isInternal()->yes()) {
|
|
|
|
// we can't know for certain so we assume its a override with purpose
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->areParameterDefaultsDifferent($classMethod, $parentMethodReflection)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function areParameterDefaultsDifferent(
|
|
|
|
ClassMethod $classMethod,
|
|
|
|
MethodReflection $methodReflection
|
|
|
|
): bool {
|
|
|
|
$parameterReflections = $this->methodReflectionProvider->getParameterReflectionsFromMethodReflection(
|
|
|
|
$methodReflection
|
|
|
|
);
|
|
|
|
|
|
|
|
foreach ($parameterReflections as $key => $parameterReflection) {
|
|
|
|
if (! isset($classMethod->params[$key])) {
|
|
|
|
if ($parameterReflection->getDefaultValue() !== null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$methodParam = $classMethod->params[$key];
|
|
|
|
|
|
|
|
if ($this->parameterDefaultsComparator->areDefaultValuesDifferent(
|
|
|
|
$parameterReflection,
|
|
|
|
$methodParam
|
|
|
|
)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|