Passed
Push — master ( 165520...cfabca )
by Théo
12:07 queued 10:05
created

AddPrefixCommand::execute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1.0005

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 12
nc 1
nop 1
dl 0
loc 20
ccs 11
cts 12
cp 0.9167
crap 1.0005
rs 9.8666
c 2
b 0
f 0
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\Console\ConfigLoader;
26
use Humbug\PhpScoper\Console\ConsoleScoper;
27
use Humbug\PhpScoper\Scoper;
28
use Humbug\PhpScoper\Scoper\ConfigurableScoper;
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 count;
35
use function is_dir;
36
use function is_writable;
37
use function Safe\getcwd;
38
use function Safe\sprintf;
39
use const DIRECTORY_SEPARATOR;
40
41
final class AddPrefixCommand implements Command, CommandAware
42
{
43
    use CommandAwareness;
44
45
    private const PATH_ARG = 'paths';
46
    private const PREFIX_OPT = 'prefix';
47
    private const OUTPUT_DIR_OPT = 'output-dir';
48
    private const FORCE_OPT = 'force';
49
    private const STOP_ON_FAILURE_OPT = 'stop-on-failure';
50
    private const CONFIG_FILE_OPT = 'config';
51
    private const DEFAULT_CONFIG_FILE_PATH = 'scoper.inc.php';
52
    private const NO_CONFIG_OPT = 'no-config';
53
54
    private Filesystem $fileSystem;
55 16
    private ConfigurableScoper $scoper;
56
    private bool $init = false;
57 16
    private Application $application;
58
59 16
    public function __construct(
60 16
        Filesystem $fileSystem,
61
        Scoper $scoper,
62
        Application $application
63
    ) {
64
        $this->fileSystem = $fileSystem;
65
        $this->scoper = new ConfigurableScoper($scoper);
66 16
        $this->application = $application;
67
    }
68 16
69
    public function getConfiguration(): CommandConfiguration
70
    {
71 16
        return new CommandConfiguration(
72 16
            'add-prefix',
73 16
            'Goes through all the PHP files found in the given paths to apply the given prefix to namespaces & FQNs.',
74 16
            '',
75 16
            [
76 16
                new InputArgument(
77
                    self::PATH_ARG,
78 16
                    InputArgument::IS_ARRAY,
79 16
                    'The path(s) to process.'
80 16
                ),
81 16
            ],
82 16
            [
83
                ChangeableDirectory::createOption(),
84 16
                new InputOption(
85 16
                    self::PREFIX_OPT,
86 16
                    'p',
87 16
                    InputOption::VALUE_REQUIRED,
88 16
                    'The namespace prefix to add.',
89 16
                ),
90
                new InputOption(
91 16
                    self::OUTPUT_DIR_OPT,
92 16
                    'o',
93 16
                    InputOption::VALUE_REQUIRED,
94 16
                    'The output directory in which the prefixed code will be dumped.',
95 16
                    'build',
96
                ),
97 16
                new InputOption(
98 16
                    self::FORCE_OPT,
99 16
                    'f',
100 16
                    InputOption::VALUE_NONE,
101 16
                    'Deletes any existing content in the output directory without any warning.'
102
                ),
103 16
                new InputOption(
104 16
                    self::STOP_ON_FAILURE_OPT,
105 16
                    's',
106 16
                    InputOption::VALUE_NONE,
107 16
                    'Stops on failure.'
108 16
                ),
109 16
                new InputOption(
110
                    self::CONFIG_FILE_OPT,
111
                    'c',
112 16
                    InputOption::VALUE_REQUIRED,
113 16
                    sprintf(
114 16
                        'Conf,iguration file. Will use "%s" if found by default.',
115 16
                        self::DEFAULT_CONFIG_FILE_PATH
116 16
                    )
117
                ),
118
                new InputOption(
119
                    self::NO_CONFIG_OPT,
120
                    null,
121
                    InputOption::VALUE_NONE,
122
                    'Do not look for a configuration file.'
123
                ),
124 14
            ],
125
        );
126 14
    }
127 14
128
    public function execute(IO $io): int
129 14
    {
130
        $io->writeln('');
131 14
132 14
        ChangeableDirectory::changeWorkingDirectory($io);
133 14
134
        $paths = $this->getPathArguments($io);
135 14
        $outputDir = $this->getOutputDir($io);
136 12
137
        $config = $this->retrieveConfig($io, $paths);
138 12
139
        $this->getScoper($config)->scope(
140
            $io,
141
            $config,
142 12
            $paths,
143 12
            $outputDir,
144 12
            $io->getBooleanOption(self::STOP_ON_FAILURE_OPT),
145
        );
146
147 12
        return ExitCode::SUCCESS;
148 12
    }
149 12
150
    private function getOutputDir(IO $io): string
151
    {
152
        $outputDir = $io->getStringOption(self::OUTPUT_DIR_OPT);
153 12
154 12
        if (false === $this->fileSystem->isAbsolutePath($outputDir)) {
155 12
            $outputDir = getcwd().DIRECTORY_SEPARATOR.$outputDir;
156 12
        }
157 12
158 12
        if (false === $this->fileSystem->exists($outputDir)) {
159 12
            return $outputDir;
160 12
        }
161
162
        if (false === is_writable($outputDir)) {
163
            throw new RuntimeException(
164
                sprintf(
165
                    'Expected "<comment>%s</comment>" to be writeable.',
166
                    $outputDir
167
                )
168
            );
169
        }
170 12
171
        if ($io->getBooleanOption(self::FORCE_OPT)) {
172 12
            $this->fileSystem->remove($outputDir);
173
174
            return $outputDir;
175
        }
176
177
        $question = sprintf(
178 12
            is_dir($outputDir)
179
                ? 'The output directory "<comment>%s</comment>" already exists. Continuing will erase its content, do you wish to proceed?'
180
                : 'Expected "<comment>%s</comment>" to be a directory but found a file instead. It will be  removed, do you wish to proceed?',
181
            $outputDir,
182
        );
183
184
        $canDeleteFile = $io->confirm($question, false);
185
186
        if ($canDeleteFile) {
187
            $this->fileSystem->remove($outputDir);
188 12
        }
189
190 12
        return $outputDir;
191
    }
192 12
193 12
    /**
194
     * @param string[] $paths
195 12
     */
196 12
    private function retrieveConfig(IO $io, array $paths): Configuration
197
    {
198 12
        $configLoader = new ConfigLoader(
199 12
            $this->getCommandRegistry(),
200
            $this->fileSystem,
201
        );
202
203 12
        return $configLoader->loadConfig(
204 12
            $io,
205 12
            $io->getStringOption(self::PREFIX_OPT),
206 12
            $io->getBooleanOption(self::NO_CONFIG_OPT),
207 12
            $io->getNullableStringOption(self::CONFIG_FILE_OPT),
208 12
            self::DEFAULT_CONFIG_FILE_PATH,
209 12
            $this->init,
210 12
            $paths,
211 12
            getcwd(),
212
        );
213
    }
214
215 12
    /**
216
     * @return list<string> List of absolute paths
1 ignored issue
show
Bug introduced by
The type Humbug\PhpScoper\Console\Command\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
217 12
     */
218 12
    private function getPathArguments(IO $io): array
219
    {
220
        $cwd = getcwd();
221 12
        $fileSystem = $this->fileSystem;
222
223
        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...
224 12
            static fn (string $path) => $fileSystem->isAbsolutePath($path)
225
                ? $path
226 12
                : $cwd.DIRECTORY_SEPARATOR.$path,
227
            $io->getStringArrayArgument(self::PATH_ARG),
228
        );
229
    }
230
231
    private function getScoper(Configuration $config): ConsoleScoper
232
    {
233
        return new ConsoleScoper(
234
            $this->fileSystem,
235
            $this->application,
236 12
            $this->getInnerScoper($config),
237
        );
238
    }
239
240
    private function getInnerScoper(Configuration $config): Scoper
241
    {
242
        if (count($config->getWhitelistedFiles()) === 0) {
243
            return $this->scoper;
244
        }
245
246
        return $this->scoper->withWhitelistedFiles(
247 12
            ...$config->getWhitelistedFiles(),
248 2
        );
249 2
    }
250
}
251