Skip to content

Commit 8049f60

Browse files
committed
Updated Rector to commit 52c6ece13308d46109bd1c2f4927592244d1c7b0
rectorphp/rector-src@52c6ece Add support for adding the `#[Override]` attribute to methods implementing interfaces (#7934)
1 parent 806d6a2 commit 8049f60

2 files changed

Lines changed: 71 additions & 18 deletions

File tree

rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,17 @@ final class AddOverrideAttributeToOverriddenMethodsRector extends AbstractRector
6666
* @var string
6767
*/
6868
public const ALLOW_OVERRIDE_EMPTY_METHOD = 'allow_override_empty_method';
69+
/**
70+
* @api
71+
* @var string
72+
*/
73+
public const ADD_TO_INTERFACE_METHODS = 'add_to_interface_methods';
6974
/**
7075
* @var string
7176
*/
7277
private const OVERRIDE_CLASS = 'Override';
7378
private bool $allowOverrideEmptyMethod = \false;
79+
private bool $addToInterfaceMethods = \false;
7480
private bool $hasChanged = \false;
7581
public function __construct(ReflectionProvider $reflectionProvider, ClassAnalyzer $classAnalyzer, PhpAttributeAnalyzer $phpAttributeAnalyzer, AstResolver $astResolver, ValueResolver $valueResolver, ParentClassAnalyzer $parentClassAnalyzer)
7682
{
@@ -118,7 +124,36 @@ public function foo()
118124
}
119125
}
120126
CODE_SAMPLE
121-
, [self::ALLOW_OVERRIDE_EMPTY_METHOD => \false])]);
127+
, [self::ALLOW_OVERRIDE_EMPTY_METHOD => \false]), new ConfiguredCodeSample(<<<'CODE_SAMPLE'
128+
interface ParentInterface
129+
{
130+
public function foo();
131+
}
132+
133+
final class ChildClass implements ParentInterface
134+
{
135+
public function foo()
136+
{
137+
echo 'implements interface';
138+
}
139+
}
140+
CODE_SAMPLE
141+
, <<<'CODE_SAMPLE'
142+
interface ParentInterface
143+
{
144+
public function foo();
145+
}
146+
147+
final class ChildClass implements ParentInterface
148+
{
149+
#[\Override]
150+
public function foo()
151+
{
152+
echo 'implements interface';
153+
}
154+
}
155+
CODE_SAMPLE
156+
, [self::ADD_TO_INTERFACE_METHODS => \true])]);
122157
}
123158
/**
124159
* @return array<class-string<Node>>
@@ -133,6 +168,7 @@ public function getNodeTypes(): array
133168
public function configure(array $configuration): void
134169
{
135170
$this->allowOverrideEmptyMethod = $configuration[self::ALLOW_OVERRIDE_EMPTY_METHOD] ?? \false;
171+
$this->addToInterfaceMethods = $configuration[self::ADD_TO_INTERFACE_METHODS] ?? \false;
136172
}
137173
/**
138174
* @param Class_ $node
@@ -143,8 +179,7 @@ public function refactor(Node $node): ?Node
143179
if ($this->classAnalyzer->isAnonymousClass($node)) {
144180
return null;
145181
}
146-
// skip if no parents, nor traits, nor strinables are involved
147-
if ($node->extends === null && $node->getTraitUses() === [] && !$this->implementsStringable($node)) {
182+
if ($this->shouldSkipNode($node)) {
148183
return null;
149184
}
150185
$className = (string) $this->getName($node);
@@ -153,14 +188,14 @@ public function refactor(Node $node): ?Node
153188
}
154189
$classReflection = $this->reflectionProvider->getClass($className);
155190
$parentClassReflections = $classReflection->getParents();
191+
// interfaces are added for Stringable
192+
if ($this->addToInterfaceMethods || $this->allowOverrideEmptyMethod) {
193+
$parentClassReflections = array_merge($parentClassReflections, $classReflection->getInterfaces());
194+
}
156195
if ($this->allowOverrideEmptyMethod) {
157-
$parentClassReflections = array_merge(
158-
$parentClassReflections,
159-
$classReflection->getInterfaces(),
160-
// place on last to ensure verify method exists on parent early
161-
// for non abstract method from trait
162-
$classReflection->getTraits()
163-
);
196+
// place on last to ensure verify method exists on parent early
197+
// for non abstract method from trait
198+
$parentClassReflections = array_merge($parentClassReflections, $classReflection->getTraits());
164199
}
165200
if ($parentClassReflections === []) {
166201
return null;
@@ -233,9 +268,14 @@ private function shouldSkipClassMethod(ClassMethod $classMethod): bool
233268
}
234269
private function shouldSkipParentClassMethod(ClassReflection $parentClassReflection, ClassMethod $classMethod): bool
235270
{
236-
if ($this->allowOverrideEmptyMethod && $parentClassReflection->isBuiltIn()) {
271+
// special case for Stringable interface
272+
if ($this->allowOverrideEmptyMethod && $parentClassReflection->getName() === 'Stringable') {
237273
return \false;
238274
}
275+
// if the method is on interface, skip based on the config flag
276+
if ($parentClassReflection->isInterface()) {
277+
return !$this->addToInterfaceMethods;
278+
}
239279
// parse parent method, if it has some contents or not
240280
$parentClass = $this->astResolver->resolveClassFromClassReflection($parentClassReflection);
241281
if (!$parentClass instanceof ClassLike) {
@@ -289,13 +329,26 @@ private function resolveClassMethodFromTraitUse(ClassLike $classLike, string $me
289329
}
290330
return null;
291331
}
292-
private function implementsStringable(Class_ $class): bool
332+
// early return for the class if it does not extend anything
333+
private function shouldSkipNode(Class_ $class): bool
293334
{
294-
foreach ($class->implements as $implement) {
295-
if ($this->isName($implement, 'Stringable')) {
296-
return \true;
335+
if ($class->extends !== null) {
336+
return \false;
337+
}
338+
if ($class->getTraitUses() !== []) {
339+
return \false;
340+
}
341+
if ($this->addToInterfaceMethods && $class->implements !== []) {
342+
return \false;
343+
}
344+
// add override to Stringable if flag is set
345+
if ($this->allowOverrideEmptyMethod) {
346+
foreach ($class->implements as $implement) {
347+
if ($this->isName($implement, 'Stringable')) {
348+
return \false;
349+
}
297350
}
298351
}
299-
return \false;
352+
return \true;
300353
}
301354
}

src/Application/VersionResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ final class VersionResolver
1919
* @api
2020
* @var string
2121
*/
22-
public const PACKAGE_VERSION = '23810525658cb51d0e169c2556917f22a245bcde';
22+
public const PACKAGE_VERSION = '52c6ece13308d46109bd1c2f4927592244d1c7b0';
2323
/**
2424
* @api
2525
* @var string
2626
*/
27-
public const RELEASE_DATE = '2026-04-07 18:38:23';
27+
public const RELEASE_DATE = '2026-04-07 18:39:52';
2828
/**
2929
* @var int
3030
*/

0 commit comments

Comments
 (0)