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
38 changes: 38 additions & 0 deletions packages/solid/src/NodeFinder/ParentClassConstantNodeFinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Rector\SOLID\NodeFinder;

use PhpParser\Node\Stmt\ClassConst;
use Rector\Core\NodeContainer\ParsedNodesByType;
use Rector\NodeTypeResolver\Node\AttributeKey;

final class ParentClassConstantNodeFinder
{
/**
* @var ParsedNodesByType
*/
private $parsedNodesByType;

public function __construct(ParsedNodesByType $parsedNodesByType)
{
$this->parsedNodesByType = $parsedNodesByType;
}

public function find(string $class, string $constant): ?ClassConst
{
$classNode = $this->parsedNodesByType->findClass($class);
if ($classNode === null) {
return null;
}

/** @var string|null $parentClassName */
$parentClassName = $classNode->getAttribute(AttributeKey::PARENT_CLASS_NAME);
if ($parentClassName === null) {
return null;
}

return $this->parsedNodesByType->findClassConstant($parentClassName, $constant);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\SOLID\Analyzer\ClassConstantFetchAnalyzer;
use Rector\SOLID\NodeFinder\ParentClassConstantNodeFinder;
use Rector\SOLID\Reflection\ParentConstantReflectionResolver;
use Rector\SOLID\ValueObject\ConstantVisibility;
use ReflectionClass;
use ReflectionClassConstant;

/**
* @see \Rector\SOLID\Tests\Rector\ClassConst\PrivatizeLocalClassConstantRector\PrivatizeLocalClassConstantRectorTest
Expand All @@ -36,12 +37,26 @@ final class PrivatizeLocalClassConstantRector extends AbstractRector
*/
private $classConstantFetchAnalyzer;

/**
* @var ParentConstantReflectionResolver
*/
private $parentConstantReflectionResolver;

/**
* @var ParentClassConstantNodeFinder
*/
private $parentClassConstantNodeFinder;

public function __construct(
ParsedNodesByType $parsedNodesByType,
ClassConstantFetchAnalyzer $classConstantFetchAnalyzer
ClassConstantFetchAnalyzer $classConstantFetchAnalyzer,
ParentConstantReflectionResolver $parentConstantReflectionResolver,
ParentClassConstantNodeFinder $parentClassConstantNodeFinder
) {
$this->parsedNodesByType = $parsedNodesByType;
$this->classConstantFetchAnalyzer = $classConstantFetchAnalyzer;
$this->parentConstantReflectionResolver = $parentConstantReflectionResolver;
$this->parentClassConstantNodeFinder = $parentClassConstantNodeFinder;
}

public function getDefinition(): RectorDefinition
Expand Down Expand Up @@ -126,7 +141,7 @@ private function shouldSkip(ClassConst $classConst): bool
return true;
}

if (! $this->isAtLeastPhpVersion('7.1')) {
if (! $this->isAtLeastPhpVersion(PhpVersionFeature::CONSTANT_VISIBILITY)) {
return true;
}

Expand All @@ -135,43 +150,21 @@ private function shouldSkip(ClassConst $classConst): bool

private function findParentClassConstantAndRefactorIfPossible(string $class, string $constant): ?ConstantVisibility
{
$classNode = $this->parsedNodesByType->findClass($class);
if ($classNode !== null && $classNode->hasAttribute(AttributeKey::PARENT_CLASS_NAME)) {
/** @var string $parentClass */
$parentClass = $classNode->getAttribute(AttributeKey::PARENT_CLASS_NAME);

if ($parentClass !== '') {
$parentClassConstant = $this->parsedNodesByType->findClassConstant($parentClass, $constant);
if ($parentClassConstant !== null) {
// Make sure the parent's constant has been refactored
$this->refactor($parentClassConstant);

return new ConstantVisibility(
$parentClassConstant->isPublic(),
$parentClassConstant->isProtected(),
$parentClassConstant->isPrivate()
);
}

// If the constant isn't declared in the parent, it might be declared in the parent's parent
}
}

$reflectionClass = new ReflectionClass($class);
if (! $reflectionClass->getParentClass()) {
return null;
}

$parentClass = $reflectionClass->getParentClass();
if (! $parentClass) {
return null;
$parentClassConstant = $this->parentClassConstantNodeFinder->find($class, $constant);

if ($parentClassConstant !== null) {
// Make sure the parent's constant has been refactored
$this->refactor($parentClassConstant);

return new ConstantVisibility(
$parentClassConstant->isPublic(),
$parentClassConstant->isProtected(),
$parentClassConstant->isPrivate()
);
// If the constant isn't declared in the parent, it might be declared in the parent's parent
}

/** @var ReflectionClass $parentClass */
$parentClassConstantReflection = $this->resolveParentClassConstantReflection(
$parentClass->getName(),
$constant
);
$parentClassConstantReflection = $this->parentConstantReflectionResolver->resolve($class, $constant);
if ($parentClassConstantReflection === null) {
return null;
}
Expand Down Expand Up @@ -250,19 +243,4 @@ private function isUsedByChildrenOnly(array $useClasses, string $class): bool

return $isChild;
}

private function resolveParentClassConstantReflection(string $class, string $constant): ?ReflectionClassConstant
{
$parentReflectionClass = new ReflectionClass($class);

while ($parentReflectionClass !== false) {
if ($parentReflectionClass->hasConstant($constant)) {
return $parentReflectionClass->getReflectionConstant($constant);
}

$parentReflectionClass = $parentReflectionClass->getParentClass();
}

return null;
}
}
27 changes: 27 additions & 0 deletions packages/solid/src/Reflection/ParentConstantReflectionResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Rector\SOLID\Reflection;

use ReflectionClass;
use ReflectionClassConstant;

final class ParentConstantReflectionResolver
{
public function resolve(string $class, string $constant): ?ReflectionClassConstant
{
$reflectionClass = (new ReflectionClass($class));
$parentReflectionClass = $reflectionClass->getParentClass();

while ($parentReflectionClass !== false) {
if ($parentReflectionClass->hasConstant($constant)) {
return $parentReflectionClass->getReflectionConstant($constant);
}

$parentReflectionClass = $parentReflectionClass->getParentClass();
}

return null;
}
}
3 changes: 2 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ parameters:
level: max

# to allow installing with various phsptan versions without reporting old errors here
# reportUnmatchedIgnoredErrors: false
reportUnmatchedIgnoredErrors: false

autoload_directories:
- stubs
Expand Down Expand Up @@ -237,3 +237,4 @@ parameters:
- '#Method Rector\\FileSystemRector\\Rector\\AbstractFileSystemRector\:\:wrapToArg\(\) should return array<PhpParser\\Node\\Arg\> but returns array<PhpParser\\Node\\Arg\|PhpParser\\Node\\Expr\>#'
- '#Method Rector\\SOLID\\Rector\\ClassConst\\PrivatizeLocalClassConstantRector\:\:resolveParentClassConstantReflection\(\) should return ReflectionClassConstant\|null but returns ReflectionClassConstant\|false#'
- '#PHPDoc tag @var for variable \$parentClass contains generic class ReflectionClass but does not specify its types\: T#'
- '#Method Rector\\SOLID\\Reflection\\ParentConstantReflectionResolver\:\:(.*?)\(\) should return ReflectionClassConstant\|null but returns ReflectionClassConstant\|false#'