Completed
Push — master ( 954942...2b19b6 )
by Théo
02:40
created

AddPrefixCommand   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 168
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 8

Test Coverage

Coverage 90.24%

Importance

Changes 0
Metric Value
dl 0
loc 168
ccs 74
cts 82
cp 0.9024
rs 10
c 0
b 0
f 0
wmc 15
lcom 2
cbo 8

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
B configure() 0 24 1
B execute() 0 33 2
A validatePrefix() 0 19 3
A validatePaths() 0 18 2
B validateOutputDir() 0 40 6
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 PREFIX_ARG = 'prefix';
34
    /** @internal */
35
    const PATH_ARG = 'paths';
36
    /** @internal */
37
    const OUTPUT_DIR = 'output-dir';
38
39
    private $fileSystem;
40
    private $handle;
41
42
    /**
43
     * @inheritdoc
44
     */
45 14
    public function __construct(Filesystem $fileSystem, HandleAddPrefix $handle)
46
    {
47 14
        parent::__construct();
48
49 14
        $this->fileSystem = $fileSystem;
50 14
        $this->handle = $handle;
51 14
    }
52
53
    /**
54
     * @inheritdoc
55
     */
56 14
    protected function configure()
57
    {
58
        $this
59 14
            ->setName('add-prefix')
60 14
            ->setDescription('Goes through all the PHP files found in the given paths to apply the given prefix to namespaces & FQNs.')
61 14
            ->addArgument(
62 14
                self::PREFIX_ARG,
63 14
                InputArgument::REQUIRED,
64 14
                'The namespace prefix to add'
65
            )
66 14
            ->addArgument(
67 14
                self::PATH_ARG,
68 14
                InputArgument::REQUIRED | InputArgument::IS_ARRAY,
69 14
                'The path(s) to process.'
70
            )
71 14
            ->addOption(
72 14
                self::OUTPUT_DIR,
73 14
                'o',
74 14
                InputOption::VALUE_REQUIRED,
75 14
                'The output directory in which the prefixed code will be dumped.',
76 14
                'build'
77
            )
78
        ;
79 14
    }
80
81
    /**
82
     * @inheritdoc
83
     */
84 12
    protected function execute(InputInterface $input, OutputInterface $output)
85
    {
86 12
        $io = new SymfonyStyle($input, $output);
87
88 12
        $this->validatePrefix($input);
89 7
        $this->validatePaths($input);
90 7
        $this->validateOutputDir($input, $io);
91
92 7
        $logger = new ConsoleLogger(
93 7
            $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...
94
            $io
95
        );
96
97 7
        $logger->outputScopingStart(
98 7
            $input->getArgument(self::PREFIX_ARG),
99 7
            $input->getArgument(self::PATH_ARG)
100
        );
101
102
        try {
103 7
            $this->handle->__invoke(
104 7
                $input->getArgument(self::PREFIX_ARG),
105 7
                $input->getArgument(self::PATH_ARG),
106 7
                $input->getOption(self::OUTPUT_DIR),
107
                $logger
108
            );
109 1
        } catch (Throwable $throwable) {
110 1
            $logger->outputScopingEndWithFailure();
111
112 1
            throw $throwable;
113
        }
114
115 6
        $logger->outputScopingEnd();
116 6
    }
117
118 12
    private function validatePrefix(InputInterface $input)
119
    {
120 12
        $prefix = trim($input->getArgument(self::PREFIX_ARG));
121
122 12
        if (1 === preg_match('/(?<prefix>.*?)\\\\*$/', $prefix, $matches)) {
123 12
            $prefix = $matches['prefix'];
124
        }
125
126 12
        if ('' === $prefix) {
127 5
            throw new RuntimeException(
128
                sprintf(
129 5
                    'Expected "%s" argument to be a non empty string.',
130 5
                    self::PREFIX_ARG
131
                )
132
            );
133
        }
134
135 7
        $input->setArgument(self::PREFIX_ARG, $prefix);
136 7
    }
137
138 7
    private function validatePaths(InputInterface $input)
139
    {
140 7
        $cwd = getcwd();
141 7
        $fileSystem = $this->fileSystem;
142
143 7
        $paths = array_map(
144 7
            function (string $path) use ($cwd, $fileSystem) {
145 7
                if (false === $fileSystem->isAbsolutePath($path)) {
146 3
                    return $cwd.DIRECTORY_SEPARATOR.$path;
147
                }
148
149 7
                return $path;
150 7
            },
151 7
            $input->getArgument(self::PATH_ARG)
152
        );
153
154 7
        $input->setArgument(self::PATH_ARG, $paths);
155 7
    }
156
157 7
    private function validateOutputDir(InputInterface $input, OutputStyle $io)
158
    {
159 7
        $outputDir = $input->getOption(self::OUTPUT_DIR);
160
161 7
        if (false === $this->fileSystem->isAbsolutePath($outputDir)) {
162 2
            $outputDir = getcwd().DIRECTORY_SEPARATOR.$outputDir;
163
        }
164
165 7
        $input->setOption(self::OUTPUT_DIR, $outputDir);
166
167 7
        if (false === $this->fileSystem->exists($outputDir)) {
168 1
            return;
169
        }
170
171 6
        if (false === is_dir($outputDir)) {
172
            $canDeleteFile = $io->confirm(
173
                sprintf(
174
                    'Expected "%s" to be a directory but found a file instead. It will be removed, do you wish '
175
                    .'to proceed?',
176
                    $outputDir
177
                ),
178
                false
179
            );
180
181
            if (false === $canDeleteFile) {
182
                return;
183
            }
184
185
            $this->fileSystem->remove($outputDir);
186
        }
187
188 6
        if (false === is_writable($outputDir)) {
189
            throw new RuntimeException(
190
                sprintf(
191
                    'Expected "%s" to be writeable.',
192
                    $outputDir
193
                )
194
            );
195
        }
196 6
    }
197
}
198