Passed
Push — master ( 643bf0...e1ccb6 )
by Théo
01:57
created

AddPrefixCommand::configure()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 31
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 24
nc 1
nop 0
dl 0
loc 31
ccs 24
cts 24
cp 1
crap 1
rs 8.8571
c 0
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 Humbug\PhpScoper\Handler\HandleAddPrefix;
18
use Humbug\PhpScoper\Logger\ConsoleLogger;
19
use Symfony\Component\Console\Command\Command;
20
use Symfony\Component\Console\Exception\RuntimeException;
21
use Symfony\Component\Console\Input\InputArgument;
22
use Symfony\Component\Console\Input\InputInterface;
23
use Symfony\Component\Console\Input\InputOption;
24
use Symfony\Component\Console\Output\OutputInterface;
25
use Symfony\Component\Console\Style\OutputStyle;
26
use Symfony\Component\Console\Style\SymfonyStyle;
27
use Symfony\Component\Filesystem\Filesystem;
28
use Throwable;
29
30
final class AddPrefixCommand extends Command
31
{
32
    /** @internal */
33
    const PATH_ARG = 'paths';
34
    /** @internal */
35
    const PREFIX_OPT = 'prefix';
36
    /** @internal */
37
    const OUTPUT_DIR_OPT = 'output-dir';
38
    /** @internal */
39
    const FORCE_OPT = 'force';
40
41
    private $fileSystem;
42
    private $handle;
43
44
    /**
45
     * @inheritdoc
46
     */
47 16
    public function __construct(Filesystem $fileSystem, HandleAddPrefix $handle)
48
    {
49 16
        parent::__construct();
50
51 16
        $this->fileSystem = $fileSystem;
52 16
        $this->handle = $handle;
53 16
    }
54
55
    /**
56
     * @inheritdoc
57
     */
58 16
    protected function configure()
59
    {
60
        $this
61 16
            ->setName('add-prefix')
62 16
            ->setDescription('Goes through all the PHP files found in the given paths to apply the given prefix to namespaces & FQNs.')
63 16
            ->addArgument(
64 16
                self::PATH_ARG,
65 16
                InputArgument::IS_ARRAY,
66 16
                'The path(s) to process.'
67
            )
68 16
            ->addOption(
69 16
                self::PREFIX_OPT,
70 16
                'p',
71 16
                InputOption::VALUE_REQUIRED,
72 16
                'The namespace prefix to add'
73
            )
74 16
            ->addOption(
75 16
                self::OUTPUT_DIR_OPT,
76 16
                'o',
77 16
                InputOption::VALUE_REQUIRED,
78 16
                'The output directory in which the prefixed code will be dumped.',
79 16
                'build'
80
            )
81 16
            ->addOption(
82 16
                self::FORCE_OPT,
83 16
                'f',
84 16
                InputOption::VALUE_NONE,
85 16
                'Deletes any existing content in the output directory without any warning'
86
            )
87
        ;
88 16
    }
89
90
    /**
91
     * @inheritdoc
92
     */
93 14
    protected function execute(InputInterface $input, OutputInterface $output)
94
    {
95 14
        $io = new SymfonyStyle($input, $output);
96
97 14
        $this->validatePrefix($input);
98 9
        $this->validatePaths($input);
99 9
        $this->validateOutputDir($input, $io);
100
101 9
        $logger = new ConsoleLogger(
102 9
            $this->getApplication(),
103 9
            $io
104
        );
105
106 9
        $logger->outputScopingStart(
107 9
            $input->getOption(self::PREFIX_OPT),
108 9
            $input->getArgument(self::PATH_ARG)
109
        );
110
111
        try {
112 9
            $this->handle->__invoke(
113 9
                $input->getOption(self::PREFIX_OPT),
114 9
                $input->getArgument(self::PATH_ARG),
115 9
                $input->getOption(self::OUTPUT_DIR_OPT),
116 9
                $logger
117
            );
118 1
        } catch (Throwable $throwable) {
119 1
            $logger->outputScopingEndWithFailure();
120
121 1
            throw $throwable;
122
        }
123
124 8
        $logger->outputScopingEnd();
125 8
    }
126
127 14
    private function validatePrefix(InputInterface $input)
128
    {
129 14
        $prefix = $input->getOption(self::PREFIX_OPT);
130
131 14
        if (null === $prefix) {
132 1
            $prefix = uniqid('PhpScoper');
133
        } else {
134 13
            $prefix = trim($prefix);
135
        }
136
137 14
        if (1 === preg_match('/(?<prefix>.*?)\\\\*$/', $prefix, $matches)) {
138 14
            $prefix = $matches['prefix'];
139
        }
140
141 14
        if ('' === $prefix) {
142 5
            throw new RuntimeException(
143 5
                sprintf(
144 5
                    'Expected "%s" argument to be a non empty string.',
145 5
                    self::PREFIX_OPT
146
                )
147
            );
148
        }
149
150 9
        $input->setOption(self::PREFIX_OPT, $prefix);
151 9
    }
152
153 9
    private function validatePaths(InputInterface $input)
154
    {
155 9
        $cwd = getcwd();
156 9
        $fileSystem = $this->fileSystem;
157
158 9
        $paths = array_map(
159 9
            function (string $path) use ($cwd, $fileSystem) {
160 8
                if (false === $fileSystem->isAbsolutePath($path)) {
161 1
                    return $cwd.DIRECTORY_SEPARATOR.$path;
162
                }
163
164 8
                return $path;
165 9
            },
166 9
            $input->getArgument(self::PATH_ARG)
167
        );
168
169 9
        if (0 === count($paths)) {
170 1
            $paths[] = $cwd;
171
        }
172
173 9
        $input->setArgument(self::PATH_ARG, $paths);
174 9
    }
175
176 9
    private function validateOutputDir(InputInterface $input, OutputStyle $io)
177
    {
178 9
        $outputDir = $input->getOption(self::OUTPUT_DIR_OPT);
179
180 9
        if (false === $this->fileSystem->isAbsolutePath($outputDir)) {
181 1
            $outputDir = getcwd().DIRECTORY_SEPARATOR.$outputDir;
182
        }
183
184 9
        $input->setOption(self::OUTPUT_DIR_OPT, $outputDir);
185
186 9
        if (false === $this->fileSystem->exists($outputDir)) {
187 9
            return;
188
        }
189
190
        if (false === is_writable($outputDir)) {
191
            throw new RuntimeException(
192
                sprintf(
193
                    'Expected "<comment>%s</comment>" to be writeable.',
194
                    $outputDir
195
                )
196
            );
197
        }
198
199
        if ($input->getOption(self::FORCE_OPT)) {
200
            $this->fileSystem->remove($outputDir);
201
202
            return;
203
        }
204
205
        if (false === is_dir($outputDir)) {
206
            $canDeleteFile = $io->confirm(
207
                sprintf(
208
                    'Expected "<comment>%s</comment>" to be a directory but found a file instead. It will be '
209
                    .'removed, do you wish to proceed?',
210
                    $outputDir
211
                ),
212
                false
213
            );
214
215
            if (false === $canDeleteFile) {
216
                return;
217
            }
218
219
            $this->fileSystem->remove($outputDir);
220
        } else {
221
            $canDeleteFile = $io->confirm(
222
                sprintf(
223
                    'The output directory "<comment>%s</comment>" already exists. Continuing will erase its'
224
                    .' content, do you wish to proceed?',
225
                    $outputDir
226
                ),
227
                false
228
            );
229
230
            if (false === $canDeleteFile) {
231
                return;
232
            }
233
234
            $this->fileSystem->remove($outputDir);
235
        }
236
    }
237
}
238