Skip to content

Commit dc60329

Browse files
committed
improve parent call fixture in Php4ConstructorRector
1 parent b31f801 commit dc60329

8 files changed

Lines changed: 108 additions & 96 deletions

File tree

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@
9797
"files": [
9898
"tests/debug_functions.php",
9999
"rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/Source/some_view_function.php",
100-
"rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Source/FunctionTyped.php"
100+
"rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Source/FunctionTyped.php",
101+
"rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Source/ParentClass.php"
101102
]
102103
},
103104
"scripts": {

rules-tests/Php70/Rector/ClassMethod/Php4ConstructorRector/Fixture/method_call_parent.php.inc

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
final class SomeChildB extends ParentClass
4+
{
5+
public function SomeChildB()
6+
{
7+
$this->ParentClass();
8+
}
9+
}
10+
11+
?>
12+
-----
13+
<?php
14+
15+
final class SomeChildB extends ParentClass
16+
{
17+
public function __construct()
18+
{
19+
parent::__construct();
20+
}
21+
}
22+
23+
?>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
class ParentClass
4+
{
5+
public function ParentClass()
6+
{
7+
}
8+
}

rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,14 @@ public function getNodeTypes(): array
8585
public function refactor(Node $node): ?Node
8686
{
8787
$classMethods = $node->getMethods();
88-
8988
if ($classMethods === []) {
9089
return null;
9190
}
9291

93-
$filter = static fn (ClassMethod $classMethod): bool => $classMethod->isPrivate();
94-
$privateMethods = array_filter($classMethods, $filter);
92+
$privateMethods = array_filter(
93+
$classMethods,
94+
fn (ClassMethod $classMethod): bool => $classMethod->isPrivate()
95+
);
9596

9697
if ($privateMethods === []) {
9798
return null;

rules/Php70/NodeAnalyzer/Php4ConstructorClassMethodAnalyzer.php

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,12 @@
55
namespace Rector\Php70\NodeAnalyzer;
66

77
use PhpParser\Node\Stmt\ClassMethod;
8-
use PHPStan\Analyser\Scope;
98
use PHPStan\Reflection\ClassReflection;
109

1110
final class Php4ConstructorClassMethodAnalyzer
1211
{
13-
public function detect(ClassMethod $classMethod, Scope $scope): bool
12+
public function detect(ClassMethod $classMethod, ClassReflection $classReflection): bool
1413
{
15-
// catch only classes without namespace
16-
if ($scope->getNamespace() !== null) {
17-
return false;
18-
}
19-
2014
if ($classMethod->isAbstract()) {
2115
return false;
2216
}
@@ -25,11 +19,11 @@ public function detect(ClassMethod $classMethod, Scope $scope): bool
2519
return false;
2620
}
2721

28-
$classReflection = $scope->getClassReflection();
29-
if (! $classReflection instanceof ClassReflection) {
22+
if ($classReflection->isAnonymous()) {
3023
return false;
3124
}
3225

33-
return ! $classReflection->isAnonymous();
26+
$possiblePhp4MethodNames = [$classReflection->getName(), lcfirst($classReflection->getName())];
27+
return in_array($classMethod->name->toString(), $possiblePhp4MethodNames, true);
3428
}
3529
}

rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php

Lines changed: 66 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@
1010
use PhpParser\Node\Expr\StaticCall;
1111
use PhpParser\Node\Identifier;
1212
use PhpParser\Node\Name;
13-
use PhpParser\Node\Name\FullyQualified;
1413
use PhpParser\Node\Stmt\Class_;
1514
use PhpParser\Node\Stmt\ClassMethod;
1615
use PhpParser\Node\Stmt\Expression;
1716
use PHPStan\Analyser\Scope;
1817
use PHPStan\Reflection\ClassReflection;
1918
use Rector\Enum\ObjectReference;
2019
use Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver;
21-
use Rector\NodeTypeResolver\Node\AttributeKey;
2220
use Rector\Php70\NodeAnalyzer\Php4ConstructorClassMethodAnalyzer;
2321
use Rector\PHPStan\ScopeFetcher;
2422
use Rector\Rector\AbstractRector;
@@ -85,18 +83,10 @@ public function getNodeTypes(): array
8583
*/
8684
public function refactor(Node $node): Class_|null
8785
{
88-
$className = $this->getName($node);
89-
if (! is_string($className)) {
90-
return null;
91-
}
92-
93-
$psr4ConstructorMethod = $node->getMethod(lcfirst($className)) ?? $node->getMethod($className);
94-
if (! $psr4ConstructorMethod instanceof ClassMethod) {
95-
return null;
96-
}
97-
9886
$scope = ScopeFetcher::fetch($node);
99-
if (! $this->php4ConstructorClassMethodAnalyzer->detect($psr4ConstructorMethod, $scope)) {
87+
88+
// catch only classes without namespace
89+
if ($scope->getNamespace() !== null) {
10090
return null;
10191
}
10292

@@ -105,55 +95,61 @@ public function refactor(Node $node): Class_|null
10595
return null;
10696
}
10797

108-
// process parent call references first
109-
$this->processClassMethodStatementsForParentConstructorCalls($psr4ConstructorMethod, $scope);
110-
111-
// does it already have a __construct method?
112-
if (! $node->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod) {
113-
$psr4ConstructorMethod->name = new Identifier(MethodName::CONSTRUCT);
114-
}
115-
116-
$classMethodStmts = $psr4ConstructorMethod->stmts;
117-
if ($classMethodStmts === null) {
98+
$className = $this->getName($node);
99+
if (! is_string($className)) {
118100
return null;
119101
}
120102

121-
$parentClassName = $classReflection->getParentClass() instanceof ClassReflection
122-
? $classReflection->getParentClass()
123-
->getName()
124-
: '';
103+
foreach ($node->stmts as $classStmtKey => $classStmt) {
104+
if (! $classStmt instanceof ClassMethod) {
105+
continue;
106+
}
125107

126-
foreach ($classMethodStmts as $classMethodStmt) {
127-
if (! $classMethodStmt instanceof Expression) {
128-
return null;
108+
if (! $this->php4ConstructorClassMethodAnalyzer->detect($classStmt, $classReflection)) {
109+
continue;
129110
}
130111

131-
if ($this->isLocalMethodCallNamed($classMethodStmt->expr, MethodName::CONSTRUCT)) {
132-
$stmtKey = $psr4ConstructorMethod->getAttribute(AttributeKey::STMT_KEY);
133-
unset($node->stmts[$stmtKey]);
112+
$psr4ConstructorMethod = $classStmt;
113+
114+
// process parent call references first
115+
$this->processClassMethodStatementsForParentConstructorCalls($psr4ConstructorMethod, $scope);
116+
117+
// does it already have a __construct method?
118+
if (! $node->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod) {
119+
$psr4ConstructorMethod->name = new Identifier(MethodName::CONSTRUCT);
134120
}
135121

136-
if ($this->isLocalMethodCallNamed($classMethodStmt->expr, $parentClassName) && ! $node->getMethod(
137-
$parentClassName
138-
) instanceof ClassMethod) {
139-
/** @var MethodCall $expr */
140-
$expr = $classMethodStmt->expr;
141-
$classMethodStmt->expr = new StaticCall(new FullyQualified($parentClassName), new Identifier(
142-
MethodName::CONSTRUCT
143-
), $expr->args);
122+
$parentClassName = $this->getParentClassName($classReflection);
123+
124+
foreach ((array) $psr4ConstructorMethod->stmts as $classMethodStmt) {
125+
if (! $classMethodStmt instanceof Expression) {
126+
continue;
127+
}
128+
129+
// remove delegating method
130+
if ($this->isLocalMethodCallNamed($classMethodStmt->expr, MethodName::CONSTRUCT)) {
131+
unset($node->stmts[$classStmtKey]);
132+
}
133+
134+
if ($this->isParentMethodCall($classMethodStmt, $parentClassName, $node)) {
135+
/** @var MethodCall $expr */
136+
$expr = $classMethodStmt->expr;
137+
138+
$classMethodStmt->expr = new StaticCall(new Name(ObjectReference::PARENT), new Identifier(
139+
MethodName::CONSTRUCT
140+
), $expr->args);
141+
}
144142
}
143+
144+
return $node;
145145
}
146146

147-
return $node;
147+
return null;
148148
}
149149

150150
private function processClassMethodStatementsForParentConstructorCalls(ClassMethod $classMethod, Scope $scope): void
151151
{
152-
if (! is_iterable($classMethod->stmts)) {
153-
return;
154-
}
155-
156-
foreach ($classMethod->stmts as $methodStmt) {
152+
foreach ((array) $classMethod->stmts as $methodStmt) {
157153
if (! $methodStmt instanceof Expression) {
158154
continue;
159155
}
@@ -217,4 +213,27 @@ private function isLocalMethodCallNamed(Expr $expr, string $name): bool
217213

218214
return $this->isName($expr->name, $name);
219215
}
216+
217+
private function getParentClassName(ClassReflection $classReflection): ?string
218+
{
219+
if (! $classReflection->getParentClass() instanceof ClassReflection) {
220+
return null;
221+
}
222+
223+
return $classReflection->getParentClass()
224+
->getName();
225+
}
226+
227+
private function isParentMethodCall(Expression $classMethodStmt, ?string $parentClassName, Class_ $class): bool
228+
{
229+
if ($parentClassName === null) {
230+
return false;
231+
}
232+
233+
if ($class->getMethod($parentClassName)) {
234+
return false;
235+
}
236+
237+
return $this->isLocalMethodCallNamed($classMethodStmt->expr, $parentClassName);
238+
}
220239
}

src/NodeTypeResolver/Node/AttributeKey.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ final class AttributeKey
162162
public const IS_BYREF_RETURN = 'is_byref_return';
163163

164164
/**
165+
* @deprecated use keys directly from stmts iteration as convention
165166
* @var string
166167
*/
167168
public const STMT_KEY = 'stmt_key';

0 commit comments

Comments
 (0)