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

AddPrefixCommand::validatePaths()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 1
nop 1
dl 0
loc 18
ccs 12
cts 12
cp 1
crap 2
rs 9.4285
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 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