Completed
Push — master ( 25a5dc...9c7c1c )
by Théo
03:12
created

AddPrefixCommand::validatePaths()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 2
nop 1
dl 0
loc 22
ccs 14
cts 14
cp 1
crap 3
rs 9.2
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(),
1 ignored issue
show
Bug introduced by
It seems like $this->getApplication() can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
103
            $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
                $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
                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