Passed
Push — master ( dc8be2...e43899 )
by Théo
03:04 queued 41s
created

AddPrefixCommand::getInnerScoper()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 8
rs 10
ccs 6
cts 6
cp 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the humbug/php-scoper package.
7
 *
8
 * Copyright (c) 2017 Théo FIDRY <[email protected]>,
9
 *                    Pádraic Brady <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Humbug\PhpScoper\Console\Command;
16
17
use Fidry\Console\Application\Application;
18
use Fidry\Console\Command\Command;
19
use Fidry\Console\Command\CommandAware;
20
use Fidry\Console\Command\CommandAwareness;
21
use Fidry\Console\Command\Configuration as CommandConfiguration;
22
use Fidry\Console\ExitCode;
23
use Fidry\Console\IO;
24
use Humbug\PhpScoper\Configuration;
25
use Humbug\PhpScoper\ConfigurationFactory;
26
use Humbug\PhpScoper\Console\ConfigLoader;
27
use Humbug\PhpScoper\Console\ConsoleScoper;
28
use Humbug\PhpScoper\Scoper;
29
use Symfony\Component\Console\Exception\RuntimeException;
30
use Symfony\Component\Console\Input\InputArgument;
31
use Symfony\Component\Console\Input\InputOption;
32
use Symfony\Component\Filesystem\Filesystem;
33
use function array_map;
34
use function is_dir;
35
use function is_writable;
36
use function Safe\getcwd;
37
use function Safe\sprintf;
38
use const DIRECTORY_SEPARATOR;
39
40
final class AddPrefixCommand implements Command, CommandAware
41
{
42
    use CommandAwareness;
43
44
    private const PATH_ARG = 'paths';
45
    private const PREFIX_OPT = 'prefix';
46
    private const OUTPUT_DIR_OPT = 'output-dir';
47
    private const FORCE_OPT = 'force';
48
    private const STOP_ON_FAILURE_OPT = 'stop-on-failure';
49
    private const CONFIG_FILE_OPT = 'config';
50
    private const DEFAULT_CONFIG_FILE_PATH = 'scoper.inc.php';
51
    private const NO_CONFIG_OPT = 'no-config';
52
53
    private Filesystem $fileSystem;
54
    private Scoper $scoper;
55 16
    private bool $init = false;
56
    private Application $application;
57 16
    private ConfigurationFactory $configFactory;
58
59 16
    public function __construct(
60 16
        Filesystem $fileSystem,
61
        Scoper $scoper,
62
        Application $application,
63
        ConfigurationFactory $configFactory
64
    ) {
65
        $this->fileSystem = $fileSystem;
66 16
        $this->scoper = $scoper;
67
        $this->application = $application;
68 16
        $this->configFactory = $configFactory;
69
    }
70
71 16
    public function getConfiguration(): CommandConfiguration
72 16
    {
73 16
        return new CommandConfiguration(
74 16
            'add-prefix',
75 16
            'Goes through all the PHP files found in the given paths to apply the given prefix to namespaces & FQNs.',
76 16
            '',
77
            [
78 16
                new InputArgument(
79 16
                    self::PATH_ARG,
80 16
                    InputArgument::IS_ARRAY,
81 16
                    'The path(s) to process.'
82 16
                ),
83
            ],
84 16
            [
85 16
                ChangeableDirectory::createOption(),
86 16
                new InputOption(
87 16
                    self::PREFIX_OPT,
88 16
                    'p',
89 16
                    InputOption::VALUE_REQUIRED,
90
                    'The namespace prefix to add.',
91 16
                ),
92 16
                new InputOption(
93 16
                    self::OUTPUT_DIR_OPT,
94 16
                    'o',
95 16
                    InputOption::VALUE_REQUIRED,
96
                    'The output directory in which the prefixed code will be dumped.',
97 16
                    'build',
98 16
                ),
99 16
                new InputOption(
100 16
                    self::FORCE_OPT,
101 16
                    'f',
102
                    InputOption::VALUE_NONE,
103 16
                    'Deletes any existing content in the output directory without any warning.'
104 16
                ),
105 16
                new InputOption(
106 16
                    self::STOP_ON_FAILURE_OPT,
107 16
                    's',
108 16
                    InputOption::VALUE_NONE,
109 16
                    'Stops on failure.'
110
                ),
111
                new InputOption(
112 16
                    self::CONFIG_FILE_OPT,
113 16
                    'c',
114 16
                    InputOption::VALUE_REQUIRED,
115 16
                    sprintf(
116 16
                        'Conf,iguration file. Will use "%s" if found by default.',
117
                        self::DEFAULT_CONFIG_FILE_PATH
118
                    )
119
                ),
120
                new InputOption(
121
                    self::NO_CONFIG_OPT,
122
                    null,
123
                    InputOption::VALUE_NONE,
124 14
                    'Do not look for a configuration file.'
125
                ),
126 14
            ],
127 14
        );
128
    }
129 14
130
    public function execute(IO $io): int
131 14
    {
132 14
        $io->writeln('');
133 14
134
        ChangeableDirectory::changeWorkingDirectory($io);
135 14
136 12
        $paths = $this->getPathArguments($io);
137
        $outputDir = $this->getOutputDir($io);
138 12
139
        $config = $this->retrieveConfig($io, $paths);
140
141
        $this->getScoper()->scope(
142 12
            $io,
143 12
            $config,
144 12
            $paths,
145
            $outputDir,
146
            $io->getBooleanOption(self::STOP_ON_FAILURE_OPT),
147 12
        );
148 12
149 12
        return ExitCode::SUCCESS;
150
    }
151
152
    private function getOutputDir(IO $io): string
153 12
    {
154 12
        $outputDir = $io->getStringOption(self::OUTPUT_DIR_OPT);
155 12
156 12
        if (false === $this->fileSystem->isAbsolutePath($outputDir)) {
157 12
            $outputDir = getcwd().DIRECTORY_SEPARATOR.$outputDir;
158 12
        }
159 12
160 12
        if (false === $this->fileSystem->exists($outputDir)) {
161
            return $outputDir;
162
        }
163
164
        if (false === is_writable($outputDir)) {
165
            throw new RuntimeException(
166
                sprintf(
167
                    'Expected "<comment>%s</comment>" to be writeable.',
168
                    $outputDir
169
                )
170 12
            );
171
        }
172 12
173
        if ($io->getBooleanOption(self::FORCE_OPT)) {
174
            $this->fileSystem->remove($outputDir);
175
176
            return $outputDir;
177
        }
178 12
179
        $question = sprintf(
180
            is_dir($outputDir)
181
                ? 'The output directory "<comment>%s</comment>" already exists. Continuing will erase its content, do you wish to proceed?'
182
                : 'Expected "<comment>%s</comment>" to be a directory but found a file instead. It will be  removed, do you wish to proceed?',
183
            $outputDir,
184
        );
185
186
        $canDeleteFile = $io->confirm($question, false);
187
188 12
        if ($canDeleteFile) {
189
            $this->fileSystem->remove($outputDir);
190 12
        }
191
192 12
        return $outputDir;
193 12
    }
194
195 12
    /**
196 12
     * @param string[] $paths
197
     */
198 12
    private function retrieveConfig(IO $io, array $paths): Configuration
199 12
    {
200
        $configLoader = new ConfigLoader(
201
            $this->getCommandRegistry(),
202
            $this->fileSystem,
203 12
            $this->configFactory,
204 12
        );
205 12
206 12
        return $configLoader->loadConfig(
207 12
            $io,
208 12
            $io->getStringOption(self::PREFIX_OPT),
209 12
            $io->getBooleanOption(self::NO_CONFIG_OPT),
210 12
            $io->getNullableStringOption(self::CONFIG_FILE_OPT),
211 12
            self::DEFAULT_CONFIG_FILE_PATH,
212
            $this->init,
213
            $paths,
214
            getcwd(),
215 12
        );
216
    }
217 12
218 12
    /**
219
     * @return list<string> List of absolute paths
220
     */
221 12
    private function getPathArguments(IO $io): array
222
    {
223
        $cwd = getcwd();
224 12
        $fileSystem = $this->fileSystem;
225
226 12
        return array_map(
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_map(functio...gument(self::PATH_ARG)) returns the type array which is incompatible with the documented return type Humbug\PhpScoper\Console\Command\list.
Loading history...
227
            static fn (string $path) => $fileSystem->isAbsolutePath($path)
228
                ? $path
229
                : $cwd.DIRECTORY_SEPARATOR.$path,
230
            $io->getStringArrayArgument(self::PATH_ARG),
231
        );
232
    }
233
234
    private function getScoper(): ConsoleScoper
235
    {
236 12
        return new ConsoleScoper(
237
            $this->fileSystem,
238
            $this->application,
239
            $this->scoper,
240
        );
241
    }
242
}
243