Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector\Fixture;

// a private method called through a class-string static call, self::class::sampleClass(),
// is not detected by the usage analyzer - keep it
final class KeepSelfClassStaticCall
{
public function run(): object
{
return self::class::sampleClass();
}

private function sampleClass(): object
{
return new \stdClass();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
namespace Rector\DeadCode\Rector\ClassMethod;

use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Reflection\ClassReflection;
Expand Down Expand Up @@ -91,6 +94,10 @@ public function refactor(Node $node): ?Node

$dataProviderMethodNames = $this->resolveDataProviderMethodNames($node);

// methods invoked via a class-string static call, e.g. self::class::sampleClass(),
// are not seen by the usage analyzer
$classStringCallMethodNames = $this->resolveClassStringStaticCallNames($node);

foreach ($node->stmts as $classStmtKey => $classStmt) {
if (! $classStmt instanceof ClassMethod) {
continue;
Expand Down Expand Up @@ -118,6 +125,10 @@ public function refactor(Node $node): ?Node
continue;
}

if ($this->isNames($classMethod, $classStringCallMethodNames)) {
continue;
}

unset($node->stmts[$classStmtKey]);
$hasChanged = true;
}
Expand Down Expand Up @@ -153,6 +164,35 @@ private function shouldSkip(ClassMethod $classMethod, ClassReflection $classRefl
return $classReflection->hasMethod(MethodName::CALL);
}

/**
* @return string[]
*/
private function resolveClassStringStaticCallNames(Class_ $class): array
{
$methodNames = [];

/** @var StaticCall[] $staticCalls */
$staticCalls = $this->betterNodeFinder->findInstanceOf($class->stmts, StaticCall::class);
foreach ($staticCalls as $staticCall) {
// e.g. self::class::sampleClass() - the called class is a ::class expression
if (! $staticCall->class instanceof ClassConstFetch) {
continue;
}

if (! $this->isName($staticCall->class->name, 'class')) {
continue;
}

if (! $staticCall->name instanceof Identifier) {
continue;
}

$methodNames[] = $staticCall->name->toString();
}

return $methodNames;
}

private function hasDynamicMethodCallOnFetchThis(ClassMethod $classMethod): bool
{
return (bool) $this->betterNodeFinder->findFirst(
Expand Down
Loading