Skip to content
Merged
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,19 @@
<?php

namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\StrictArrayParamDimFetchRector\Fixture;

use ArrayAccess;

class SkipPrivateMethodCalledWithArrayAccessArg
{
public function run(ArrayAccess $arrayAccess, array $data): void
{
$this->isTrue($arrayAccess);
$this->isTrue($data);
}

private function isTrue($value): bool
{
return $value['asdf'] === true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike;
Expand All @@ -35,6 +36,7 @@
use PHPStan\Type\UnionType;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\NodeTypeResolver\TypeComparator\TypeComparator;
use Rector\PhpParser\Node\BetterNodeFinder;
use Rector\Rector\AbstractRector;
use Rector\RuleDoc\CodeSample\CodeSample;
use Rector\RuleDoc\RuleDefinition;
Expand All @@ -48,7 +50,8 @@ final class StrictArrayParamDimFetchRector extends AbstractRector
public function __construct(
private readonly ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard,
private readonly TypeComparator $typeComparator,
private readonly TypeFactory $typeFactory
private readonly TypeFactory $typeFactory,
private readonly BetterNodeFinder $betterNodeFinder
) {
}

Expand Down Expand Up @@ -111,7 +114,7 @@ public function refactor(Node $node): ?Node
return null;
}

foreach ($node->getParams() as $param) {
foreach ($node->getParams() as $position => $param) {
if ($param->type instanceof Node) {
continue;
}
Expand All @@ -128,6 +131,15 @@ public function refactor(Node $node): ?Node
continue;
}

// a private method is only reachable from inside the class, so all its callers are visible here;
// skip if any of them passes an ArrayAccess object, which also supports dim fetch
if ($node instanceof ClassMethod && $node->isPrivate() && $this->isCalledWithArrayAccessArg(
$node,
$position
)) {
continue;
}

$param->type = new Identifier('array');
$hasChanged = true;
}
Expand Down Expand Up @@ -339,6 +351,51 @@ private function isMethodCall(string $paramName, ?Node $node): bool
return false;
}

private function isCalledWithArrayAccessArg(ClassMethod $classMethod, int $position): bool
{
$class = $this->findClassOfMethod($classMethod);
if (! $class instanceof Class_) {
return false;
}

$methodName = $this->getName($classMethod);

$calls = $this->betterNodeFinder->findInstancesOf($class->getMethods(), [MethodCall::class, StaticCall::class]);

return array_any(
$calls,
function (MethodCall|StaticCall $call) use ($methodName, $position): bool {
if ($call->isFirstClassCallable()) {
return false;
}

if (! $this->isName($call->name, $methodName)) {
return false;
}

$args = $call->getArgs();
if (! isset($args[$position])) {
return false;
}

return $this->isArrayAccess($this->getType($args[$position]->value));
}
);
}

private function findClassOfMethod(ClassMethod $classMethod): ?Class_
{
$classes = $this->betterNodeFinder->findInstanceOf($this->getFile()->getNewStmts(), Class_::class);

foreach ($classes as $class) {
if (in_array($classMethod, $class->getMethods(), true)) {
return $class;
}
}

return null;
}

private function isArrayAccess(Type $type): bool
{
if (! $type instanceof ObjectType) {
Expand Down
Loading