Test Failed
Pull Request — master (#38)
by Théo
02:36
created

AddPrefixCommand::configure()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 31
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 24
nc 1
nop 0
dl 0
loc 31
ccs 20
cts 20
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 14
     * @inheritdoc
46
     */
47 14
    public function __construct(Filesystem $fileSystem, HandleAddPrefix $handle)
48
    {
49 14
        parent::__construct();
50 14
51 14
        $this->fileSystem = $fileSystem;
52
        $this->handle = $handle;
53
    }
54
55
    /**
56 14
     * @inheritdoc
57
     */
58
    protected function configure()
59 14
    {
60 14
        $this
61 14
            ->setName('add-prefix')
62 14
            ->setDescription('Goes through all the PHP files found in the given paths to apply the given prefix to namespaces & FQNs.')
63 14
            ->addArgument(
64 14
                self::PATH_ARG,
65
                InputArgument::IS_ARRAY,
66 14
                'The path(s) to process.'
67 14
            )
68 14
            ->addOption(
69 14
                self::PREFIX_OPT,
70
                'p',
71 14
                InputOption::VALUE_REQUIRED,
72 14
                'The namespace prefix to add'
73 14
            )
74 14
            ->addOption(
75 14
                self::OUTPUT_DIR_OPT,
76 14
                'o',
77
                InputOption::VALUE_REQUIRED,
78
                'The output directory in which the prefixed code will be dumped.',
79 14
                'build'
80
            )
81
            ->addOption(
82
                self::FORCE_OPT,
83
                'f',
84 12
                InputOption::VALUE_NONE,
85
                'Deletes any existing content in the output directory without any warning'
86 12
            )
87
        ;
88 12
    }
89 7
90 7
    /**
91
     * @inheritdoc
92 7
     */
93 7
    protected function execute(InputInterface $input, OutputInterface $output)
94
    {
95
        $io = new SymfonyStyle($input, $output);
96
97 7
        $this->validatePrefix($input);
98 7
        $this->validatePaths($input);
99 7
        $this->validateOutputDir($input, $io);
100
101
        $logger = new ConsoleLogger(
102
            $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 7
            $io
104 7
        );
105 7
106 7
        $logger->outputScopingStart(
107
            $input->getOption(self::PREFIX_OPT),
108
            $input->getArgument(self::PATH_ARG)
109 1
        );
110 1
111
        try {
112 1
            $this->handle->__invoke(
113
                $input->getOption(self::PREFIX_OPT),
114
                $input->getArgument(self::PATH_ARG),
115 6
                $input->getOption(self::OUTPUT_DIR_OPT),
116 6
                $logger
117
            );
118 12
        } catch (Throwable $throwable) {
119
            $logger->outputScopingEndWithFailure();
120 12
121
            throw $throwable;
122 12
        }
123 12
124
        $logger->outputScopingEnd();
125
    }
126 12
127 5
    private function validatePrefix(InputInterface $input)
128
    {
129 5
        $prefix = $input->getOption(self::PREFIX_OPT);
130 5
131
        if (null === $prefix) {
132
            $prefix = uniqid('PhpScoper');
133
        } else {
134
            $prefix = trim($prefix);
135 7
        }
136 7
137
        if (1 === preg_match('/(?<prefix>.*?)\\\\*$/', $prefix, $matches)) {
138 7
            $prefix = $matches['prefix'];
139
        }
140 7
141 7
        if ('' === $prefix) {
142
            throw new RuntimeException(
143 7
                sprintf(
144 7
                    'Expected "%s" argument to be a non empty string.',
145 7
                    self::PREFIX_OPT
146 3
                )
147
            );
148
        }
149 7
150 7
        $input->setOption(self::PREFIX_OPT, $prefix);
151 7
    }
152
153
    private function validatePaths(InputInterface $input)
154 7
    {
155 7
        $cwd = getcwd();
156
        $fileSystem = $this->fileSystem;
157 7
158
        $paths = array_map(
159 7
            function (string $path) use ($cwd, $fileSystem) {
160
                if (false === $fileSystem->isAbsolutePath($path)) {
161 7
                    return $cwd.DIRECTORY_SEPARATOR.$path;
162 2
                }
163
164
                return $path;
165 7
            },
166
            $input->getArgument(self::PATH_ARG)
167 7
        );
168 1
169
        if (0 === count($paths)) {
170
            $paths[] = $cwd;
171 6
        }
172
173
        $input->setArgument(self::PATH_ARG, $paths);
174
    }
175
176
    private function validateOutputDir(InputInterface $input, OutputStyle $io)
177
    {
178
        $outputDir = $input->getOption(self::OUTPUT_DIR_OPT);
179
180
        if (false === $this->fileSystem->isAbsolutePath($outputDir)) {
181
            $outputDir = getcwd().DIRECTORY_SEPARATOR.$outputDir;
182
        }
183
184
        $input->setOption(self::OUTPUT_DIR_OPT, $outputDir);
185
186
        if (false === $this->fileSystem->exists($outputDir)) {
187
            return;
188 6
        }
189
190
        if (false === is_writable($outputDir)) {
191
            throw new RuntimeException(
192
                sprintf(
193
                    'Expected "%s" to be writeable.',
194
                    $outputDir
195
                )
196 6
            );
197
        }
198
199
        if ($input->hasOption(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