<commit_before><?php declare(strict_types=1);

namespace Rector\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
use Rector\CodingStyle\Naming\ClassNaming;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\ConfiguredCodeSample;
use Rector\RectorDefinition\RectorDefinition;

final class RenameClassRector extends AbstractRector
{
    /**
     * @var string[]
     */
    private $oldToNewClasses = [];

    /**
     * @var DocBlockManipulator
     */
    private $docBlockManipulator;

    /**
     * @var string[]
     */
    private $alreadyProcessedClasses = [];

    /**
     * @var ClassNaming
     */
    private $classNaming;

    /**
     * @param string[] $oldToNewClasses
     */
    public function __construct(
        DocBlockManipulator $docBlockManipulator,
        ClassNaming $classNaming,
        array $oldToNewClasses = []
    ) {
        $this->docBlockManipulator = $docBlockManipulator;
        $this->classNaming = $classNaming;
        $this->oldToNewClasses = $oldToNewClasses;
    }

    public function getDefinition(): RectorDefinition
    {
        return new RectorDefinition('Replaces defined classes by new ones.', [
            new ConfiguredCodeSample(
                <<<'CODE_SAMPLE'
namespace App;

use SomeOldClass;

function someFunction(SomeOldClass $someOldClass): SomeOldClass
{
    if ($someOldClass instanceof SomeOldClass) {
        return new SomeOldClass; 
    }
}
CODE_SAMPLE
                ,
                <<<'CODE_SAMPLE'
namespace App;

use SomeNewClass;

function someFunction(SomeNewClass $someOldClass): SomeNewClass
{
    if ($someOldClass instanceof SomeNewClass) {
        return new SomeNewClass;
    }
}
CODE_SAMPLE
                ,
                [
                    'App\SomeOldClass' => 'App\SomeNewClass',
                ]
            ),
        ]);
    }

    /**
     * @return string[]
     */
    public function getNodeTypes(): array
    {
        return [
            Name::class,
            Property::class,
            FunctionLike::class,
            Expression::class,
            ClassLike::class,
            Namespace_::class,
        ];
    }

    /**
     * @param Name|FunctionLike|Property $node
     */
    public function refactor(Node $node): ?Node
    {
        // replace on @var/@param/@return/@throws
        foreach ($this->oldToNewClasses as $oldClass => $newClass) {
            $this->docBlockManipulator->changeType($node, $oldClass, $newClass);
        }

        if ($node instanceof Name) {
            return $this->refactorName($node);
        }

        if ($node instanceof Namespace_) {
            $node = $this->refactorNamespaceNode($node);
        }

        if ($node instanceof ClassLike) {
            $node = $this->refactorClassLikeNode($node);
        }

        if ($node === null) {
            return null;
        }

        return $node;
    }

    /**
     * Checks validity:
     *
     * - extends SomeClass<commit_msg>speedup RenameClassRector on doc comments (#1425)<commit_after><?php declare(strict_types=1);

namespace Rector\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
use Rector\CodingStyle\Naming\ClassNaming;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\ConfiguredCodeSample;
use Rector\RectorDefinition\RectorDefinition;

final class RenameClassRector extends AbstractRector
{
    /**
     * @var string[]
     */
    private $oldToNewClasses = [];

    /**
     * @var DocBlockManipulator
     */
    private $docBlockManipulator;

    /**
     * @var string[]
     */
    private $alreadyProcessedClasses = [];

    /**
     * @var ClassNaming
     */
    private $classNaming;

    /**
     * @param string[] $oldToNewClasses
     */
    public function __construct(
        DocBlockManipulator $docBlockManipulator,
        ClassNaming $classNaming,
        array $oldToNewClasses = []
    ) {
        $this->docBlockManipulator = $docBlockManipulator;
        $this->classNaming = $classNaming;
        $this->oldToNewClasses = $oldToNewClasses;
    }

    public function getDefinition(): RectorDefinition
    {
        return new RectorDefinition('Replaces defined classes by new ones.', [
            new ConfiguredCodeSample(
                <<<'CODE_SAMPLE'
namespace App;

use SomeOldClass;

function someFunction(SomeOldClass $someOldClass): SomeOldClass
{
    if ($someOldClass instanceof SomeOldClass) {
        return new SomeOldClass; 
    }
}
CODE_SAMPLE
                ,
                <<<'CODE_SAMPLE'
namespace App;

use SomeNewClass;

function someFunction(SomeNewClass $someOldClass): SomeNewClass
{
    if ($someOldClass instanceof SomeNewClass) {
        return new SomeNewClass;
    }
}
CODE_SAMPLE
                ,
                [
                    'App\SomeOldClass' => 'App\SomeNewClass',
                ]
            ),
        ]);
    }

    /**
     * @return string[]
     */
    public function getNodeTypes(): array
    {
        return [
            Name::class,
            Property::class,
            FunctionLike::class,
            Expression::class,
            ClassLike::class,
            Namespace_::class,
        ];
    }

    /**
     * @param Name|FunctionLike|Property $node
     */
    public function refactor(Node $node): ?Node
    {
        // replace on @var/@param/@return/@throws
        if ($node->getDocComment()) {
            foreach ($this->oldToNewClasses as $oldClass => $newClass) {
                $this->docBlockManipulator->changeType($node, $oldClass, $newClass);
            }
        }

        if ($node instanceof Name) {
            return $this->refactorName($node);
        }

        if ($node instanceof Namespace_) {
            $node = $this->refactorNamespaceNode($node);
        }

        if ($node instanceof ClassLike) {
            $node = $this->refactorClassLikeNode($node);
        }

        if ($node === null) {
            return null;
        }

        return $node;
    }

    /**
     * Checks validity:
     *
     * - extends SomeClass
{'fraction_non_alphanumeric': 0.11015981735159817, 'fraction_numerical': 0.0008561643835616438, 'mean_word_length': 2.3202273803884417, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1546002', 'n_tokens_mistral': 2104, 'n_tokens_neox': 2062, 'n_words': 390}