Skip to content

Commit

Permalink
[Php83] Add implements interface support on AddOverrideAttributeToOve…
Browse files Browse the repository at this point in the history
…rriddenMethodsRector (#5429)
  • Loading branch information
samsonasik committed Jan 4, 2024
1 parent 1f9829d commit b007a8b
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Rector\Tests\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector\Fixture;

use Rector\Tests\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector\Source\ExampleFromInterface;

class ApplyAttributeToOverrideMethodFromInterface implements ExampleFromInterface
{
public function foo()
{
}
}

?>
-----
<?php

namespace Rector\Tests\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector\Fixture;

use Rector\Tests\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector\Source\ExampleFromInterface;

class ApplyAttributeToOverrideMethodFromInterface implements ExampleFromInterface
{
#[\Override]
public function foo()
{
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Rector\Tests\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector\Source;

interface ExampleFromInterface
{
public function foo();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use Rector\NodeAnalyzer\ClassAnalyzer;
use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer;
Expand All @@ -24,6 +25,8 @@
*/
final class AddOverrideAttributeToOverriddenMethodsRector extends AbstractRector implements MinPhpVersionInterface
{
private bool $hasChanged = false;

public function __construct(
private readonly ReflectionProvider $reflectionProvider,
private readonly ClassAnalyzer $classAnalyzer,
Expand Down Expand Up @@ -87,40 +90,23 @@ public function getNodeTypes(): array
*/
public function refactor(Node $node): ?Node
{
// Detect if class extends a parent class
if ($this->shouldSkipClass($node)) {
$this->hasChanged = false;

if ($this->classAnalyzer->isAnonymousClass($node)) {
return null;
}

// Fetch the parent class reflection
$parentClassReflection = $this->reflectionProvider->getClass((string) $node->extends);

$hasChanged = false;

foreach ($node->getMethods() as $classMethod) {
if ($classMethod->name->toString() === '__construct') {
continue;
}

// Private methods should be ignored
if ($parentClassReflection->hasNativeMethod($classMethod->name->toString())) {
// ignore if it is a private method on the parent
$parentMethod = $parentClassReflection->getNativeMethod($classMethod->name->toString());
if ($parentMethod->isPrivate()) {
continue;
}
$className = (string) $this->getName($node);
if (! $this->reflectionProvider->hasClass($className)) {
return null;
}

// ignore if it already uses the attribute
if ($this->phpAttributeAnalyzer->hasPhpAttribute($classMethod, 'Override')) {
continue;
}
$classReflection = $this->reflectionProvider->getClass($className);
$parentClassReflections = [...$classReflection->getParents(), ...$classReflection->getInterfaces()];

$classMethod->attrGroups[] = new AttributeGroup([new Attribute(new FullyQualified('Override'))]);
$hasChanged = true;
}
}
$this->processAddOverrideAttribute($node, $parentClassReflections);

if (! $hasChanged) {
if (! $this->hasChanged) {
return null;
}

Expand All @@ -132,16 +118,42 @@ public function provideMinPhpVersion(): int
return PhpVersionFeature::OVERRIDE_ATTRIBUTE;
}

private function shouldSkipClass(Class_ $class): bool
/**
* @param ClassReflection[] $parentClassReflections
*/
private function processAddOverrideAttribute(Class_ $class, array $parentClassReflections): void
{
if ($this->classAnalyzer->isAnonymousClass($class)) {
return true;
if ($parentClassReflections === []) {
return;
}

if (! $class->extends instanceof FullyQualified) {
return true;
}
foreach ($class->getMethods() as $classMethod) {
if ($classMethod->name->toString() === '__construct') {
continue;
}

// ignore if it already uses the attribute
if ($this->phpAttributeAnalyzer->hasPhpAttribute($classMethod, 'Override')) {
continue;
}

// Private methods should be ignored
foreach ($parentClassReflections as $parentClassReflection) {
if (! $parentClassReflection->hasNativeMethod($classMethod->name->toString())) {
continue;
}

return ! $this->reflectionProvider->hasClass($class->extends->toString());
// ignore if it is a private method on the parent
$parentMethod = $parentClassReflection->getNativeMethod($classMethod->name->toString());
if ($parentMethod->isPrivate()) {
continue;
}

$classMethod->attrGroups[] = new AttributeGroup([new Attribute(new FullyQualified('Override'))]);
$this->hasChanged = true;

continue 2;
}
}
}
}

0 comments on commit b007a8b

Please sign in to comment.