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

namespace Rector\DeprecationExtractor\Console\Command;

use Rector\DeprecationExtractor\Deprecation\DeprecationCollector;
use Rector\DeprecationExtractor\DeprecationExtractor;
use Rector\DeprecationExtractor\Rector\RectorGuesser;
use Rector\DeprecationExtractor\RectorGuess\RectorGuess;
use Rector\Naming\CommandNaming;
use Rector\Node\Attribute;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
 * This commands shows suggested rector to use to resolve deprecation.
 *
 * We need this manually to analyze other versions and do not actually create any rector.
 */
final class ExtractDeprecationsCommand extends Command
{
    /**
     * @var string
     */
    private const ARGUMENT_SOURCE_NAME = 'source';

    /**
     * @var DeprecationExtractor
     */
    private $deprecationExtractor;

    /**
     * @var DeprecationCollector
     */
    private $deprecationCollector;

    /**
     * @var SymfonyStyle
     */
    private $symfonyStyle;

    /**
     * @var RectorGuesser
     */
    private $rectorGuesser;

    public function __construct(
        DeprecationExtractor $deprecationExtractor,
        DeprecationCollector $deprecationCollector,
        RectorGuesser $rectorGuesser,
        SymfonyStyle $symfonyStyle
    ) {
        $this->deprecationExtractor = $deprecationExtractor;
        $this->deprecationCollector = $deprecationCollector;
        $this->rectorGuesser = $rectorGuesser;
        $this->symfonyStyle = $symfonyStyle;

        parent::__construct();
    }

    protected function configure(): void
    {
        $this->setName(CommandNaming::classToName(self::class));
        $this->setDescription('Extract deprecation notes from PHP files in directory(ies).');
        $this->addArgument(
            self::ARGUMENT_SOURCE_NAME,
            InputArgument::REQUIRED | InputArgument::IS_ARRAY,
            'One or more directory to be checked.'
        );
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->deprecationExtractor->scanDirectories(
            $input->getArgument(self::ARGUMENT_SOURCE_NAME)
        );

        $this->symfonyStyle->title(sprintf(
            'Found %d deprecations',
            count($this->deprecationCollector->getDeprecations())
        ));

        $guessedRectors = $this->rectorGuesser->guessForDeprecations($this->deprecationCollector->getDeprecations());

        foreach ($guessedRectors as $guessedRector) {
            if ($guessedRector->isUseful() === false) {
                continue;
            }

            $this->renderGuessedRector($guessedRector);
        }

        return 0;<commit_msg>[DeprecationExtractor] add filtering for useless RectorGuesses<commit_after><?php declare(strict_types=1);

namespace Rector\DeprecationExtractor\Console\Command;

use Rector\DeprecationExtractor\Deprecation\DeprecationCollector;
use Rector\DeprecationExtractor\DeprecationExtractor;
use Rector\DeprecationExtractor\Rector\RectorGuesser;
use Rector\DeprecationExtractor\RectorGuess\RectorGuess;
use Rector\Naming\CommandNaming;
use Rector\Node\Attribute;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
 * This commands shows suggested rector to use to resolve deprecation.
 *
 * We need this manually to analyze other versions and do not actually create any rector.
 */
final class ExtractDeprecationsCommand extends Command
{
    /**
     * @var string
     */
    private const ARGUMENT_SOURCE_NAME = 'source';

    /**
     * @var DeprecationExtractor
     */
    private $deprecationExtractor;

    /**
     * @var DeprecationCollector
     */
    private $deprecationCollector;

    /**
     * @var SymfonyStyle
     */
    private $symfonyStyle;

    /**
     * @var RectorGuesser
     */
    private $rectorGuesser;

    public function __construct(
        DeprecationExtractor $deprecationExtractor,
        DeprecationCollector $deprecationCollector,
        RectorGuesser $rectorGuesser,
        SymfonyStyle $symfonyStyle
    ) {
        $this->deprecationExtractor = $deprecationExtractor;
        $this->deprecationCollector = $deprecationCollector;
        $this->rectorGuesser = $rectorGuesser;
        $this->symfonyStyle = $symfonyStyle;

        parent::__construct();
    }

    protected function configure(): void
    {
        $this->setName(CommandNaming::classToName(self::class));
        $this->setDescription('Extract deprecation notes from PHP files in directory(ies).');
        $this->addArgument(
            self::ARGUMENT_SOURCE_NAME,
            InputArgument::REQUIRED | InputArgument::IS_ARRAY,
            'One or more directory to be checked.'
        );
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->deprecationExtractor->scanDirectories(
            $input->getArgument(self::ARGUMENT_SOURCE_NAME)
        );

        $guessedRectors = $this->rectorGuesser->guessForDeprecations($this->deprecationCollector->getDeprecations());
        $guessedRectors = $this->filterOutUsefulGuessedRectors($guessedRectors);
        $guessedRectors = $this->filterOutDuplicatedGuessedRectors($guessedRectors);

        $this->symfonyStyle->title(sprintf(
            'Found %d useful deprecations',
            count($guessedRectors)
        ));

        foreach ($guessedRectors as $guessedRector) {
            // filter out duplicated deprecations, trigger_error + @deprecated annotation

            // $this->renderGuessedRector($guessedRector);
        }

        return 0;
    }

    /**
     * @param RectorGuess[] $guessedRectors
     * @return RectorGuess[]
     */
    private function filterOutUsefulGuessedRectors(array $guessedRectors): array
    {
        return array_filter($guessedRectors, function (RectorGuess $rectorGuess) {
            return $rectorGuess->isUseful();
        });
    }

    /**
     * @param RectorGuess[] $guessedRectors
     * @return RectorGuess[]
     */
    private function filterOutDuplicatedGuessedRectors(array $guessedRectors): array
    {
        $allMessages = [];
        foreach ($guessedRectors as $rectorGuess) {
            $allMessages[] = $rectorGuess->getMessage();
        }

        $filteredGuessedRectors = [];
        foreach ($guessedRectors as $rectorGuess) {
            foreach ($allMessages as $message) {
                // experimental; maybe count from message length?
                $levenshtein = levenshtein($rectorGuess->getMessage(), $message);
                if ($levenshtein !== 0 && $levenshtein < 10) {
                    continue 2;
                }
            }

            $filteredGuessedRectors[] = $rectorGuess;
        }

        return $filteredGuessedRectors;
{'fraction_non_alphanumeric': 0.10107647141059695, 'fraction_numerical': 0.0011184118551656648, 'mean_word_length': 2.9944165270798435, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 45, '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': '2564952', 'n_tokens_mistral': 2075, 'n_tokens_neox': 2036, 'n_words': 373}