<commit_before><?php

declare(strict_types=1);

/*
 * This file is part of the humbug/php-scoper package.
 *
 * Copyright (c) 2017 <NAME> <<EMAIL>>,
 *                    <NAME> <<EMAIL>>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Humbug\PhpScoper\Console\Command;

use Humbug\PhpScoper\Autoload\ScoperAutoloadGenerator;
use Humbug\PhpScoper\Configuration;
use Humbug\PhpScoper\Logger\ConsoleLogger;
use Humbug\PhpScoper\Scoper;
use Humbug\PhpScoper\Throwable\Exception\ParsingException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\OutputStyle;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Filesystem\Filesystem;
use Throwable;
use function Humbug\PhpScoper\get_common_path;

final class AddPrefixCommand extends BaseCommand
{
    private const PATH_ARG = 'paths';
    private const PREFIX_OPT = 'prefix';
    private const OUTPUT_DIR_OPT = 'output-dir';
    private const FORCE_OPT = 'force';
    private const STOP_ON_FAILURE_OPT = 'stop-on-failure';
    private const CONFIG_FILE_OPT = 'config';
    private const CONFIG_FILE_DEFAULT = 'scoper.inc.php';
    private const NO_CONFIG_OPT = 'no-config';

    private $fileSystem;
    private $scoper;
    private $init = false;

    /**
     * @inheritdoc
     */
    public function __construct(Filesystem $fileSystem, Scoper $scoper)
    {
        parent::__construct();

        $this->fileSystem = $fileSystem;
        $this->scoper = $scoper;
    }

    /**
     * @inheritdoc
     */
    protected function configure(): void
    {
        parent::configure();

        $this
            ->setName('add-prefix')
            ->setDescription('Goes through all the PHP files found in the given paths to apply the given prefix to namespaces & FQNs.')
            ->addArgument(
                self::PATH_ARG,
                InputArgument::IS_ARRAY,
                'The path(s) to process.'
            )
            ->addOption(
                self::PREFIX_OPT,
                'p',
                InputOption::VALUE_REQUIRED,
                'The namespace prefix to add.'
            )
            ->addOption(
                self::OUTPUT_DIR_OPT,
                'o',
                InputOption::VALUE_REQUIRED,
                'The output directory in which the prefixed code will be dumped.',
                'build'
            )
            ->addOption(
                self::FORCE_OPT,
                'f',
                InputOption::VALUE_NONE,
                'Deletes any existing content in the output directory without any warning.'
            )
            ->addOption(
                self::STOP_ON_FAILURE_OPT,
                's',
                InputOption::VALUE_NONE,
                'Stops on failure.'
            )
            ->addOption(
                self::CONFIG_FILE_OPT,
                'c',
                InputOption::VALUE_REQUIRED,
                sprintf(
                    'Configuration file. Will use "%s" if found by default.',
                    self::CONFIG_FILE_DEFAULT
                )
            )
            ->addOption(
                self::NO_CONFIG_OPT,
                null,
                InputOption::VALUE_NONE,
            'Do not look for a configuration file.'
            )
        ;
    }

    /**
     * @inheritdoc
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $io->writeln('');

        $this->changeWorkingDirectory($input);

        $this->validatePrefix($input);
        $this->validatePaths($input);
        $this->validateOutputDir($input, $io);

        $config = $this->retrieveConfig($input, $output, $io);
        $output = $input->getOption(self::OUTPUT_DIR_OPT);

        $logger = new ConsoleLogger(
            $this->getApplication(),
            $io
        );

        $logger->outputScopingStart(
            $config->getPrefix(),
            $input->getArgument(self::PATH_ARG)
        );

        try {
            $this->scopeFiles(
                $config->getPrefix(),
                $config->getFilesWithContents(),
                $output,
                $config->getPatchers(),
                $config->getWhitelist(),
                $input->getOption(self::STOP_ON_FAILURE_OPT),
                $logger
            );
        } catch (Throwable $throwable) {
            $this->fileSystem->remove($output);

            $logger->outputScopingEndWithFailure();

            throw $throwable;
        }

        $logger->outputScopingEnd();

        return 0;
    }

    private function scopeFiles(
        string $prefix,
        array $filesWithContents,
        string $output,
        array $patchers,
        array $whitelist,
        bool $stopOnFailure,
        ConsoleLogger $logger
    ): void {
        // Creates output directory if does not already exist
        $this->fileSystem->mkdir($output);

        $logger->outputFileCount(count($filesWithContents));

        $vendorDirs = [];
        $commonPath = get_common_path(array_keys($filesWithContents));

        foreach ($filesWithContents as $fileWithContents) {
            [$inputFilePath, $inputContents] = $fileWithContents;

            $outputFilePath = $output.str_replace($commonPath, '', $inputFilePath);

            if (preg_match('~((?:.*)\/vendor)\/.*~', $outputFilePath, $matches)) {
                $vendorDirs[$matches[1]] = true;
            }

            $this->scopeFile(
                $inputFilePath,
                $inputContents,
                $outputFilePath,
                $prefix,
                $patchers,
                $whitelist,
                $stopOnFailure,<commit_msg>Destructure array in foreach loops (#204)<commit_after><?php

declare(strict_types=1);

/*
 * This file is part of the humbug/php-scoper package.
 *
 * Copyright (c) 2017 <NAME> <<EMAIL>>,
 *                    <NAME> <<EMAIL>>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Humbug\PhpScoper\Console\Command;

use Humbug\PhpScoper\Autoload\ScoperAutoloadGenerator;
use Humbug\PhpScoper\Configuration;
use Humbug\PhpScoper\Logger\ConsoleLogger;
use Humbug\PhpScoper\Scoper;
use Humbug\PhpScoper\Throwable\Exception\ParsingException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\OutputStyle;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Filesystem\Filesystem;
use Throwable;
use function Humbug\PhpScoper\get_common_path;

final class AddPrefixCommand extends BaseCommand
{
    private const PATH_ARG = 'paths';
    private const PREFIX_OPT = 'prefix';
    private const OUTPUT_DIR_OPT = 'output-dir';
    private const FORCE_OPT = 'force';
    private const STOP_ON_FAILURE_OPT = 'stop-on-failure';
    private const CONFIG_FILE_OPT = 'config';
    private const CONFIG_FILE_DEFAULT = 'scoper.inc.php';
    private const NO_CONFIG_OPT = 'no-config';

    private $fileSystem;
    private $scoper;
    private $init = false;

    /**
     * @inheritdoc
     */
    public function __construct(Filesystem $fileSystem, Scoper $scoper)
    {
        parent::__construct();

        $this->fileSystem = $fileSystem;
        $this->scoper = $scoper;
    }

    /**
     * @inheritdoc
     */
    protected function configure(): void
    {
        parent::configure();

        $this
            ->setName('add-prefix')
            ->setDescription('Goes through all the PHP files found in the given paths to apply the given prefix to namespaces & FQNs.')
            ->addArgument(
                self::PATH_ARG,
                InputArgument::IS_ARRAY,
                'The path(s) to process.'
            )
            ->addOption(
                self::PREFIX_OPT,
                'p',
                InputOption::VALUE_REQUIRED,
                'The namespace prefix to add.'
            )
            ->addOption(
                self::OUTPUT_DIR_OPT,
                'o',
                InputOption::VALUE_REQUIRED,
                'The output directory in which the prefixed code will be dumped.',
                'build'
            )
            ->addOption(
                self::FORCE_OPT,
                'f',
                InputOption::VALUE_NONE,
                'Deletes any existing content in the output directory without any warning.'
            )
            ->addOption(
                self::STOP_ON_FAILURE_OPT,
                's',
                InputOption::VALUE_NONE,
                'Stops on failure.'
            )
            ->addOption(
                self::CONFIG_FILE_OPT,
                'c',
                InputOption::VALUE_REQUIRED,
                sprintf(
                    'Configuration file. Will use "%s" if found by default.',
                    self::CONFIG_FILE_DEFAULT
                )
            )
            ->addOption(
                self::NO_CONFIG_OPT,
                null,
                InputOption::VALUE_NONE,
            'Do not look for a configuration file.'
            )
        ;
    }

    /**
     * @inheritdoc
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $io->writeln('');

        $this->changeWorkingDirectory($input);

        $this->validatePrefix($input);
        $this->validatePaths($input);
        $this->validateOutputDir($input, $io);

        $config = $this->retrieveConfig($input, $output, $io);
        $output = $input->getOption(self::OUTPUT_DIR_OPT);

        $logger = new ConsoleLogger(
            $this->getApplication(),
            $io
        );

        $logger->outputScopingStart(
            $config->getPrefix(),
            $input->getArgument(self::PATH_ARG)
        );

        try {
            $this->scopeFiles(
                $config->getPrefix(),
                $config->getFilesWithContents(),
                $output,
                $config->getPatchers(),
                $config->getWhitelist(),
                $input->getOption(self::STOP_ON_FAILURE_OPT),
                $logger
            );
        } catch (Throwable $throwable) {
            $this->fileSystem->remove($output);

            $logger->outputScopingEndWithFailure();

            throw $throwable;
        }

        $logger->outputScopingEnd();

        return 0;
    }

    private function scopeFiles(
        string $prefix,
        array $filesWithContents,
        string $output,
        array $patchers,
        array $whitelist,
        bool $stopOnFailure,
        ConsoleLogger $logger
    ): void {
        // Creates output directory if does not already exist
        $this->fileSystem->mkdir($output);

        $logger->outputFileCount(count($filesWithContents));

        $vendorDirs = [];
        $commonPath = get_common_path(array_keys($filesWithContents));

        foreach ($filesWithContents as [$inputFilePath, $inputContents]) {
            $outputFilePath = $output.str_replace($commonPath, '', $inputFilePath);

            if (preg_match('~((?:.*)\/vendor)\/.*~', $outputFilePath, $matches)) {
                $vendorDirs[$matches[1]] = true;
            }

            $this->scopeFile(
                $inputFilePath,
                $inputContents,
                $outputFilePath,
                $prefix,
                $patchers,
                $whitelist,
                $stopOnFailure,
{'fraction_non_alphanumeric': 0.10861330698287221, 'fraction_numerical': 0.0013998682476943347, 'mean_word_length': 1.8861692015209126, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 87, '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': '3386969', 'n_tokens_mistral': 3353, 'n_tokens_neox': 3194, 'n_words': 682}