Skip to content

Commit e0f2acf

Browse files
committed
add fixture to keep protected method
1 parent d71688c commit e0f2acf

File tree

11 files changed

+161
-36
lines changed

11 files changed

+161
-36
lines changed

rules-tests/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector/Fixture/keep_magic_parent_called_method.php.inc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,19 @@
33
namespace Rector\Tests\Privatization\Rector\ClassMethod\PrivatizeFinalClassMethodRector\Fixture;
44

55
use PhpParser\PrettyPrinter\Standard;
6+
<<<<<<< HEAD
7+
=======
8+
use Rector\PhpParser\Node\FileNode;
9+
>>>>>>> fb58990e39 (extract LaravelClassName)
610

711
final class KeepMagicParentCalledMethod extends Standard
812
{
913
// called from parent by $this->{'p'} method
14+
<<<<<<< HEAD
1015
protected function pFileNode($fileNode)
16+
=======
17+
protected function pFileNode(FileNode $fileNode)
18+
>>>>>>> fb58990e39 (extract LaravelClassName)
1119
{
1220
}
1321
}

rules/CodingStyle/Rector/FunctionLike/FunctionLikeToFirstClassCallableRector.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,66 @@ public function getNodeTypes(): array
5454
*/
5555
public function refactor(Node $node): null|CallLike
5656
{
57+
<<<<<<< HEAD
5758
throw new ShouldNotHappenException(sprintf(
5859
'"%s" rule is deprecated. It was split into "%s" and "%s" rules.',
5960
self::class,
6061
ClosureDelegatingCallToFirstClassCallableRector::class,
6162
ArrowFunctionDelegatingCallToFirstClassCallableRector::class
6263
));
64+
=======
65+
if ($node instanceof Assign) {
66+
// @todo handle by existing attribute already
67+
if ($node->expr instanceof Closure || $node->expr instanceof ArrowFunction) {
68+
$node->expr->setAttribute(self::IS_IN_ASSIGN, true);
69+
}
70+
71+
return null;
72+
}
73+
74+
if ($node instanceof CallLike) {
75+
if ($node->isFirstClassCallable()) {
76+
return null;
77+
}
78+
79+
$methodReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node);
80+
foreach ($node->getArgs() as $arg) {
81+
if (! $arg->value instanceof Closure && ! $arg->value instanceof ArrowFunction) {
82+
continue;
83+
}
84+
85+
if ($methodReflection instanceof NativeFunctionReflection) {
86+
$parametersAcceptors = ParametersAcceptorSelector::combineAcceptors(
87+
$methodReflection->getVariants()
88+
);
89+
foreach ($parametersAcceptors->getParameters() as $extendedParameterReflection) {
90+
if ($extendedParameterReflection->getType() instanceof CallableType && $extendedParameterReflection->getType()->isVariadic()) {
91+
$arg->value->setAttribute(self::HAS_CALLBACK_SIGNATURE_MULTI_PARAMS, true);
92+
}
93+
}
94+
95+
return null;
96+
}
97+
98+
$arg->value->setAttribute(self::HAS_CALLBACK_SIGNATURE_MULTI_PARAMS, true);
99+
}
100+
101+
return null;
102+
}
103+
104+
$callLike = $this->extractCallLike($node);
105+
if ($callLike === null) {
106+
return null;
107+
}
108+
109+
if ($this->shouldSkip($node, $callLike, ScopeFetcher::fetch($node))) {
110+
return null;
111+
}
112+
113+
$callLike->args = [new VariadicPlaceholder()];
114+
115+
return $callLike;
116+
>>>>>>> 24ed2fa7ac (add fixture to keep protected method)
63117
}
64118

65119
public function provideMinPhpVersion(): int

rules/Naming/Naming/UseImportsResolver.php

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,25 +61,4 @@ public function resolvePrefix(Use_|GroupUse $use): string
6161
? $use->prefix . '\\'
6262
: '';
6363
}
64-
65-
// private function resolveNamespace(): Namespace_|null
66-
// {
67-
// /** @var File|null $file */
68-
// $file = $this->currentFileProvider->getFile();
69-
// if (! $file instanceof File) {
70-
// return null;
71-
// }
72-
//
73-
// $newStmts = $file->getNewStmts();
74-
// if ($newStmts === []) {
75-
// return null;
76-
// }
77-
//
78-
// if ($newStmts[0] instanceof FileNode) {
79-
// $fileNode = $newStmts[0];
80-
// return $fileNode->getNamespace();
81-
// }
82-
//
83-
// return null;
84-
// }
8564
}

rules/Privatization/Rector/ClassMethod/PrivatizeFinalClassMethodRector.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ public function __construct(
3131
private readonly OverrideByParentClassGuard $overrideByParentClassGuard,
3232
private readonly BetterNodeFinder $betterNodeFinder,
3333
private readonly LaravelModelGuard $laravelModelGuard,
34+
<<<<<<< HEAD
3435
private readonly ParentClassMagicCallGuard $parentClassMagicCallGuard,
36+
=======
37+
private readonly \Rector\Privatization\VisibilityGuard\ParentClassMagicCallGuard $parentClassMagicCallGuard
38+
>>>>>>> 4263eff61e (skip parent magic called method in PrivatizeFinalClassMethodRector)
3539
) {
3640
}
3741

@@ -85,6 +89,7 @@ public function refactor(Node $node): ?Node
8589
}
8690

8791
$scope = ScopeFetcher::fetch($node);
92+
8893
$classReflection = $scope->getClassReflection();
8994
if (! $classReflection instanceof ClassReflection) {
9095
return null;
@@ -108,6 +113,10 @@ public function refactor(Node $node): ?Node
108113
continue;
109114
}
110115

116+
if ($this->parentClassMagicCallGuard->containsParentClassMagicCall($node)) {
117+
continue;
118+
}
119+
111120
if ($this->classMethodVisibilityGuard->isClassMethodVisibilityGuardedByTrait(
112121
$classMethod,
113122
$classReflection
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Privatization\VisibilityGuard;
6+
7+
use PhpParser\Node\Expr;
8+
use PhpParser\Node\Expr\MethodCall;
9+
use PhpParser\Node\Stmt\Class_;
10+
use Rector\NodeNameResolver\NodeNameResolver;
11+
use Rector\PhpParser\AstResolver;
12+
use Rector\PhpParser\Node\BetterNodeFinder;
13+
14+
final class ParentClassMagicCallGuard
15+
{
16+
/**
17+
* @var array<string, bool>
18+
*/
19+
private array $cachedContainsByClassName = [];
20+
21+
public function __construct(
22+
private readonly NodeNameResolver $nodeNameResolver,
23+
private readonly AstResolver $astResolver,
24+
private readonly BetterNodeFinder $betterNodeFinder
25+
) {
26+
}
27+
28+
/**
29+
* E.g. parent class has $this->{$magicName} call that might call the protected method
30+
* If we make it private, it will break the code
31+
*/
32+
public function containsParentClassMagicCall(Class_ $class): bool
33+
{
34+
// cache as heavy AST parsing here
35+
$className = $this->nodeNameResolver->getName($class);
36+
if (isset($this->cachedContainsByClassName[$className])) {
37+
return $this->cachedContainsByClassName[$className];
38+
}
39+
40+
if ($class->extends === null) {
41+
return false;
42+
}
43+
44+
$parentClassName = $this->nodeNameResolver->getName($class->extends);
45+
46+
$parentClass = $this->astResolver->resolveClassFromName($parentClassName);
47+
if (! $parentClass instanceof Class_) {
48+
$this->cachedContainsByClassName[$className] = false;
49+
return false;
50+
}
51+
52+
foreach ($parentClass->getMethods() as $classMethod) {
53+
if ($classMethod->isAbstract()) {
54+
continue;
55+
}
56+
57+
/** @var MethodCall[] $methodCalls */
58+
$methodCalls = $this->betterNodeFinder->findInstancesOfScoped(
59+
(array) $classMethod->stmts,
60+
MethodCall::class
61+
);
62+
foreach ($methodCalls as $methodCall) {
63+
if ($methodCall->name instanceof Expr) {
64+
$this->cachedContainsByClassName[$className] = true;
65+
return true;
66+
}
67+
}
68+
}
69+
70+
return $this->containsParentClassMagicCall($parentClass);
71+
}
72+
}

src/PhpParser/Node/CustomNode/FileWithoutNamespace.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
/**
1111
* @deprecated Use @see \Rector\PhpParser\Node\FileNode instead
12+
* @api
1213
*
1314
* Inspired by https://github.com/phpstan/phpstan-src/commit/ed81c3ad0b9877e6122c79b4afda9d10f3994092
1415
*/

src/PhpParser/Node/FileNode.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@ public function __construct(
2828

2929
}
3030

31+
/**
32+
* This triggers Printed method with "pFileNode" name
33+
* @see \Rector\PhpParser\Printer\BetterStandardPrinter::pStmt_FileNode()
34+
*/
3135
public function getType(): string
3236
{
33-
return 'CustomNode_File';
37+
return 'Stmt_FileNode';
3438
}
3539

3640
/**

src/PhpParser/Printer/BetterStandardPrinter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ protected function p(
158158
return $content;
159159
}
160160

161-
protected function pCustomNode_File(FileNode $fileNode): string
161+
protected function pStmt_FileNode(FileNode $fileNode): string
162162
{
163163
return $this->pStmts($fileNode->stmts, true);
164164
}

src/PostRector/Rector/ClassRenamingPostRector.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,8 @@ public function enterNode(Node $node): Namespace_|FileNode|int|null
5858
return $node;
5959
}
6060

61-
// nothing else to handle here
61+
// nothing else to handle here, as first 2 nodes we'll hit are handled above
6262
return NodeVisitor::STOP_TRAVERSAL;
63-
//
64-
// /**
65-
// * We stop the traversal because all the work has already been done in the beforeTraverse() function
66-
// *
67-
// * Using STOP_TRAVERSAL is usually dangerous as it will stop the processing of all your nodes for all visitors
68-
// * but since the PostFileProcessor is using direct new NodeTraverser() and traverse() for only a single
69-
// * visitor per execution, using stop traversal here is safe,
70-
// * ref https://github.com/rectorphp/rector-src/blob/fc1e742fa4d9861ccdc5933f3b53613b8223438d/src/PostRector/Application/PostFileProcessor.php#L59-L61
71-
// */
72-
// return NodeVisitor::STOP_TRAVERSAL;
7363
}
7464

7565
public function shouldTraverse(array $stmts): bool

src/Testing/TestingParser/TestingParser.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public function parseFilePathToFile(string $filePath): File
3131
<<<<<<< HEAD
3232
<<<<<<< HEAD
3333
[$file, $stmts] = $this->parseToFileAndStmts($filePath);
34+
<<<<<<< HEAD
3435
=======
3536
// needed for PHPStan reflection, as it caches the last processed file
3637
$this->dynamicSourceLocatorProvider->setFilePath($filePath);
@@ -51,6 +52,8 @@ public function parseFilePathToFile(string $filePath): File
5152
[$file, $stmts] = $this->parseToFileAndStmts($filePath);
5253
>>>>>>> 8e51776f69 (cleanup phpstan errors)
5354

55+
=======
56+
>>>>>>> 90002f8aee (extract LaravelClassName)
5457
return $file;
5558
}
5659

@@ -60,8 +63,8 @@ public function parseFilePathToFile(string $filePath): File
6063
public function parseFileToDecoratedNodes(string $filePath): array
6164
{
6265
[$file, $stmts] = $this->parseToFileAndStmts($filePath);
63-
6466
return $stmts;
67+
<<<<<<< HEAD
6568
<<<<<<< HEAD
6669
}
6770

@@ -85,6 +88,8 @@ public function parseFileToDecoratedNodes(string $filePath): array
8588
// $this->currentFileProvider->setFile($file);
8689
//
8790
// return $stmts;
91+
=======
92+
>>>>>>> 90002f8aee (extract LaravelClassName)
8893
}
8994
9095
/**
@@ -105,8 +110,11 @@ private function parseToFileAndStmts(string $filePath): array
105110
=======
106111
// wrap in FileNode to enable file-level rules
107112
$stmts = [new FileNode($stmts)];
113+
<<<<<<< HEAD
108114

109115
>>>>>>> 2751658832 (introduce FileNode to handle file-level changes; deprecate IncreaseDeclareStrictTypesRector)
116+
=======
117+
>>>>>>> 90002f8aee (extract LaravelClassName)
110118
$stmts = $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($filePath, $stmts);
111119

112120
$file->hydrateStmtsAndTokens($stmts, $stmts, []);

0 commit comments

Comments
 (0)