Passed
Branch master (72182d)
by Pádraic
02:17
created

AddPrefixCommand::validateOutputDir()   B

Complexity

Conditions 8
Paths 14

Size

Total Lines 61
Code Lines 35

Duplication

Lines 3
Ratio 4.92 %

Code Coverage

Tests 7
CRAP Score 38.5117

Importance

Changes 0
Metric Value
cc 8
eloc 35
nc 14
nop 2
dl 3
loc 61
ccs 7
cts 32
cp 0.2188
crap 38.5117
rs 7.0047
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 Humbug\PhpScoper\Console\Configuration;
18
use Humbug\PhpScoper\Handler\HandleAddPrefix;
19
use Humbug\PhpScoper\Logger\ConsoleLogger;
20
use Symfony\Component\Console\Command\Command;
21
use Symfony\Component\Console\Exception\RuntimeException;
22
use Symfony\Component\Console\Input\InputArgument;
23
use Symfony\Component\Console\Input\InputInterface;
24
use Symfony\Component\Console\Input\InputOption;
25
use Symfony\Component\Console\Output\OutputInterface;
26
use Symfony\Component\Console\Style\OutputStyle;
27
use Symfony\Component\Console\Style\SymfonyStyle;
28
use Symfony\Component\Filesystem\Filesystem;
29
use Throwable;
30
31
final class AddPrefixCommand extends Command
32
{
33
    /** @internal */
34
    const PATH_ARG = 'paths';
35
    /** @internal */
36
    const PREFIX_OPT = 'prefix';
37
    /** @internal */
38
    const OUTPUT_DIR_OPT = 'output-dir';
39
    /** @internal */
40
    const FORCE_OPT = 'force';
41
    /** @internal */
42
    const STOP_ON_FAILURE_OPT = 'stop-on-failure';
43
    /** @internal */
44
    const CONFIG_FILE = 'config';
45
    /** @internal */
46
    const CONFIG_FILE_DEFAULT = 'scoper.inc.php';
47
48
    private $fileSystem;
49
    private $handle;
50
51
    /**
52
     * @inheritdoc
53
     */
54 21
    public function __construct(Filesystem $fileSystem, HandleAddPrefix $handle)
55
    {
56 21
        parent::__construct();
57
58 21
        $this->fileSystem = $fileSystem;
59 21
        $this->handle = $handle;
60
    }
61
62
    /**
63
     * @inheritdoc
64
     */
65 21
    protected function configure()
66
    {
67
        $this
68 21
            ->setName('add-prefix')
69 21
            ->setDescription('Goes through all the PHP files found in the given paths to apply the given prefix to namespaces & FQNs.')
70 21
            ->addArgument(
71 21
                self::PATH_ARG,
72 21
                InputArgument::IS_ARRAY,
73 21
                'The path(s) to process.'
74
            )
75 21
            ->addOption(
76 21
                self::PREFIX_OPT,
77 21
                'p',
78 21
                InputOption::VALUE_REQUIRED,
79 21
                'The namespace prefix to add.'
80
            )
81 21
            ->addOption(
82 21
                self::OUTPUT_DIR_OPT,
83 21
                'o',
84 21
                InputOption::VALUE_REQUIRED,
85 21
                'The output directory in which the prefixed code will be dumped.',
86 21
                'build'
87
            )
88 21
            ->addOption(
89 21
                self::FORCE_OPT,
90 21
                'f',
91 21
                InputOption::VALUE_NONE,
92 21
                'Deletes any existing content in the output directory without any warning.'
93
            )
94 21
            ->addOption(
95 21
                self::STOP_ON_FAILURE_OPT,
96 21
                's',
97 21
                InputOption::VALUE_NONE,
98 21
                'Stops on failure.'
99
            )
100 21
            ->addOption(
101 21
                self::CONFIG_FILE,
102 21
                'c',
103 21
                InputOption::VALUE_REQUIRED,
104 21
                sprintf(
105 21
                    'Configuration file. Will use "%s" if found by default',
106 21
                    self::CONFIG_FILE_DEFAULT
107
                ),
108 21
                null
109
            )
110
        ;
111
    }
112
113
    /**
114
     * @inheritdoc
115
     */
116 19
    protected function execute(InputInterface $input, OutputInterface $output)
117
    {
118 19
        $io = new SymfonyStyle($input, $output);
119
120 19
        $this->validatePrefix($input);
121 14
        $this->validatePaths($input);
122 14
        $this->validateOutputDir($input, $io);
123
124 14
        $config = $this->retrieveConfig($input, $io);
125
126 12
        $logger = new ConsoleLogger(
127 12
            $this->getApplication(),
128 12
            $io
129
        );
130
131 12
        $logger->outputScopingStart(
132 12
            $input->getOption(self::PREFIX_OPT),
133 12
            $input->getArgument(self::PATH_ARG)
134
        );
135
136
        try {
137 12
            $this->handle->__invoke(
138 12
                $input->getOption(self::PREFIX_OPT),
139 12
                $input->getArgument(self::PATH_ARG),
140 12
                $input->getOption(self::OUTPUT_DIR_OPT),
141 12
                $config->getPatchers(),
142 12
                $config->getGlobalNamespaceWhitelisters(),
143 12
                $input->getOption(self::STOP_ON_FAILURE_OPT),
144 12
                $logger
145
            );
146 1
        } catch (Throwable $throwable) {
147 1
            $logger->outputScopingEndWithFailure();
148
149 1
            throw $throwable;
150
        }
151
152 11
        $logger->outputScopingEnd();
153
    }
154
155 19
    private function validatePrefix(InputInterface $input)
156
    {
157 19
        $prefix = $input->getOption(self::PREFIX_OPT);
158
159 19
        if (null === $prefix) {
160 1
            $prefix = uniqid('PhpScoper');
161
        } else {
162 18
            $prefix = trim($prefix);
163
        }
164
165 19
        if (1 === preg_match('/(?<prefix>.*?)\\\\*$/', $prefix, $matches)) {
166 19
            $prefix = $matches['prefix'];
167
        }
168
169 19
        if ('' === $prefix) {
170 5
            throw new RuntimeException(
171 5
                sprintf(
172 5
                    'Expected "%s" argument to be a non empty string.',
173 5
                    self::PREFIX_OPT
174
                )
175
            );
176
        }
177
178 14
        $input->setOption(self::PREFIX_OPT, $prefix);
179
    }
180
181 14
    private function validatePaths(InputInterface $input)
182
    {
183 14
        $cwd = getcwd();
184 14
        $fileSystem = $this->fileSystem;
185
186 14
        $paths = array_map(
187 14
            function (string $path) use ($cwd, $fileSystem) {
188 12
                if (false === $fileSystem->isAbsolutePath($path)) {
189 1
                    return $cwd.DIRECTORY_SEPARATOR.$path;
190
                }
191
192 12
                return $path;
193 14
            },
194 14
            $input->getArgument(self::PATH_ARG)
195
        );
196
197 14
        if (0 === count($paths)) {
198 2
            $paths[] = $cwd;
199
        }
200
201 14
        $input->setArgument(self::PATH_ARG, $paths);
202
    }
203
204 14
    private function validateOutputDir(InputInterface $input, OutputStyle $io)
205
    {
206 14
        $outputDir = $input->getOption(self::OUTPUT_DIR_OPT);
207
208 14 View Code Duplication
        if (false === $this->fileSystem->isAbsolutePath($outputDir)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209 1
            $outputDir = getcwd().DIRECTORY_SEPARATOR.$outputDir;
210
        }
211
212 14
        $input->setOption(self::OUTPUT_DIR_OPT, $outputDir);
213
214 14
        if (false === $this->fileSystem->exists($outputDir)) {
215 14
            return;
216
        }
217
218
        if (false === is_writable($outputDir)) {
219
            throw new RuntimeException(
220
                sprintf(
221
                    'Expected "<comment>%s</comment>" to be writeable.',
222
                    $outputDir
223
                )
224
            );
225
        }
226
227
        if ($input->getOption(self::FORCE_OPT)) {
228
            $this->fileSystem->remove($outputDir);
229
230
            return;
231
        }
232
233
        if (false === is_dir($outputDir)) {
234
            $canDeleteFile = $io->confirm(
235
                sprintf(
236
                    'Expected "<comment>%s</comment>" to be a directory but found a file instead. It will be '
237
                    .'removed, do you wish to proceed?',
238
                    $outputDir
239
                ),
240
                false
241
            );
242
243
            if (false === $canDeleteFile) {
244
                return;
245
            }
246
247
            $this->fileSystem->remove($outputDir);
248
        } else {
249
            $canDeleteFile = $io->confirm(
250
                sprintf(
251
                    'The output directory "<comment>%s</comment>" already exists. Continuing will erase its'
252
                    .' content, do you wish to proceed?',
253
                    $outputDir
254
                ),
255
                false
256
            );
257
258
            if (false === $canDeleteFile) {
259
                return;
260
            }
261
262
            $this->fileSystem->remove($outputDir);
263
        }
264
    }
265
266 14
    private function retrieveConfig(InputInterface $input, OutputStyle $io): Configuration
267
    {
268 14
        $configFile = $input->getOption(self::CONFIG_FILE);
269
270 14
        if (null === $configFile) {
271 13
            $configFile = $this->makeAbsolutePath(self::CONFIG_FILE_DEFAULT);
272
273 13
            if (false === file_exists($configFile)) {
274 3
                $io->writeln(
275 3
                    sprintf(
276 3
                        'Patch file "%s" not found. Skipping.',
277 3
                        $configFile
278
                    ),
279 3
                    OutputStyle::VERBOSITY_DEBUG
280
                );
281
282 13
                return Configuration::load(null);
283
            }
284
        } else {
285 1
            $configFile = $this->makeAbsolutePath($configFile);
286
        }
287
288 11
        if (false === file_exists($configFile)) {
289 1
            throw new RuntimeException(
290 1
                sprintf(
291 1
                    'Could not find the file "%s".',
292 1
                    $configFile
293
                )
294
            );
295
        }
296
297 10
        $io->writeln(
298 10
            sprintf(
299 10
                'Using the configuration file "%s".',
300 10
                $configFile
301
            ),
302 10
            OutputStyle::VERBOSITY_DEBUG
303
        );
304
305 10
        return Configuration::load($configFile);
306
    }
307
308 14
    private function makeAbsolutePath(string $path): string
309
    {
310 14 View Code Duplication
        if (false === $this->fileSystem->isAbsolutePath($path)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
311 11
            $path = getcwd().DIRECTORY_SEPARATOR.$path;
312
        }
313
314 14
        return $path;
315
    }
316
}
317