Passed
Push — master ( de864b...a5423c )
by Théo
02:25
created

Process::execute()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 66
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 42
dl 0
loc 66
rs 8.9368
c 0
b 0
f 0
cc 5
nc 12
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box\Console\Command;
16
17
use function array_map;
18
use function array_shift;
19
use function array_unshift;
20
use function explode;
21
use function get_class;
22
use function getcwd;
23
use Humbug\PhpScoper\Whitelist;
24
use function implode;
25
use const KevinGH\Box\BOX_ALLOW_XDEBUG;
26
use KevinGH\Box\Compactor;
27
use KevinGH\Box\Compactor\PhpScoper;
28
use KevinGH\Box\Compactor\Placeholder;
29
use KevinGH\Box\Compactors;
30
use KevinGH\Box\Configuration;
31
use function KevinGH\Box\FileSystem\file_contents;
32
use function KevinGH\Box\FileSystem\make_path_absolute;
33
use function KevinGH\Box\FileSystem\make_path_relative;
34
use KevinGH\Box\PhpSettingsHandler;
35
use function putenv;
36
use function sprintf;
37
use stdClass;
38
use function strlen;
39
use function substr;
40
use Symfony\Component\Console\Input\InputArgument;
41
use Symfony\Component\Console\Input\InputInterface;
42
use Symfony\Component\Console\Input\InputOption;
43
use Symfony\Component\Console\Logger\ConsoleLogger;
44
use Symfony\Component\Console\Output\OutputInterface;
45
use Symfony\Component\Console\Style\SymfonyStyle;
46
use Symfony\Component\VarDumper\Cloner\VarCloner;
47
use Symfony\Component\VarDumper\Dumper\CliDumper;
48
49
final class Process extends ConfigurableCommand
50
{
51
    use ChangeableWorkingDirectory;
52
53
    private const FILE_ARGUMENT = 'file';
54
55
    private const NO_RESTART_OPTION = 'no-restart';
56
    private const NO_CONFIG_OPTION = 'no-config';
57
58
    /**
59
     * {@inheritdoc}
60
     */
61
    protected function configure(): void
62
    {
63
        parent::configure();
64
65
        $this->setName('process');
66
        $this->setDescription('⚡  Applies the registered compactors and replacement values on a file');
67
        $this->setHelp(
68
            'The <info>%command.name%</info> command will apply the registered compactors and replacement values '
69
            .'on the the given file. This is useful to debug the scoping of a specific file for example.'
70
        );
71
72
        $this->addArgument(
73
            self::FILE_ARGUMENT,
74
            InputArgument::REQUIRED,
75
            'Path to the file to process'
76
        );
77
        $this->addOption(
78
            self::NO_RESTART_OPTION,
79
            null,
80
            InputOption::VALUE_NONE,
81
            'Do not restart the PHP process. Box restarts the process by default to disable xdebug'
82
        );
83
        $this->addOption(
84
            self::NO_CONFIG_OPTION,
85
            null,
86
            InputOption::VALUE_NONE,
87
            'Ignore the config file even when one is specified with the --config option'
88
        );
89
90
        $this->configureWorkingDirOption();
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    protected function execute(InputInterface $input, OutputInterface $output): void
97
    {
98
        $io = new SymfonyStyle($input, $output);
99
100
        if ($input->getOption(self::NO_RESTART_OPTION)) {
101
            putenv(BOX_ALLOW_XDEBUG.'=1');
102
        }
103
104
        (new PhpSettingsHandler(new ConsoleLogger($output)))->check();
105
106
        $this->changeWorkingDirectory($input);
107
108
        $io->newLine();
109
110
        $config = $input->getOption(self::NO_CONFIG_OPTION)
111
            ? Configuration::create(null, new stdClass())
112
            : $this->getConfig($input, $output, true)
113
        ;
114
115
        $filePath = $input->getArgument(self::FILE_ARGUMENT);
116
117
        $path = make_path_relative($filePath, $config->getBasePath());
0 ignored issues
show
Bug introduced by
It seems like $filePath can also be of type null and string[]; however, parameter $path of KevinGH\Box\FileSystem\make_path_relative() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

117
        $path = make_path_relative(/** @scrutinizer ignore-type */ $filePath, $config->getBasePath());
Loading history...
118
119
        $compactors = $this->retrieveCompactors($config);
120
121
        $fileContents = file_contents(
122
            $absoluteFilePath = make_path_absolute(
123
                $filePath,
0 ignored issues
show
Bug introduced by
It seems like $filePath can also be of type null and string[]; however, parameter $path of KevinGH\Box\FileSystem\make_path_absolute() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

123
                /** @scrutinizer ignore-type */ $filePath,
Loading history...
124
                getcwd()
125
            )
126
        );
127
128
        $io->writeln([
129
            sprintf(
130
                '⚡  Processing the contents of the file <info>%s</info>',
131
                $absoluteFilePath
132
            ),
133
            '',
134
        ]);
135
136
        $this->logPlaceholders($io, $config);
137
        $this->logCompactors($io, $compactors);
138
139
        $fileProcessedContents = $compactors->compactContents($path, $fileContents);
140
141
        if ($io->isQuiet()) {
142
            $io->writeln($fileProcessedContents, OutputInterface::VERBOSITY_QUIET);
143
        } else {
144
            $whitelist = $this->retrieveWhitelist($compactors);
145
146
            $io->writeln([
147
                'Processed contents:',
148
                '',
149
                '<comment>"""</comment>',
150
                $fileProcessedContents,
151
                '<comment>"""</comment>',
152
            ]);
153
154
            if (null !== $whitelist) {
155
                $io->writeln([
156
                    '',
157
                    'Whitelist:',
158
                    '',
159
                    '<comment>"""</comment>',
160
                    $this->exportWhitelist($whitelist, $output),
161
                    '<comment>"""</comment>',
162
                ]);
163
            }
164
        }
165
    }
166
167
    private function retrieveCompactors(Configuration $config): Compactors
168
    {
169
        $compactors = $config->getCompactors();
170
171
        array_unshift(
172
            $compactors,
173
            new Placeholder($config->getReplacements())
174
        );
175
176
        return new Compactors(...$compactors);
177
    }
178
179
    private function logPlaceholders(SymfonyStyle $io, Configuration $config): void
180
    {
181
        if ([] === $config->getReplacements()) {
182
            $io->writeln([
183
                'No replacement values registered',
184
                '',
185
            ]);
186
187
            return;
188
        }
189
190
        $io->writeln('Registered replacement values:');
191
192
        foreach ($config->getReplacements() as $key => $value) {
193
            $io->writeln(
194
                sprintf(
195
                    '  <comment>+</comment> %s: %s',
196
                    $key,
197
                    $value
198
                )
199
            );
200
        }
201
202
        $io->newLine();
203
    }
204
205
    private function logCompactors(SymfonyStyle $io, Compactors $compactors): void
206
    {
207
        $nestedCompactors = $compactors->toArray();
208
209
        foreach ($nestedCompactors as $index => $compactor) {
210
            if ($compactor instanceof Placeholder) {
211
                unset($nestedCompactors[$index]);
212
            }
213
        }
214
215
        if ([] === $nestedCompactors) {
216
            $io->writeln([
217
                'No compactor registered',
218
                '',
219
            ]);
220
221
            return;
222
        }
223
224
        $io->writeln('Registered compactors:');
225
226
        $logCompactors = static function (Compactor $compactor) use ($io): void {
227
            $compactorClassParts = explode('\\', get_class($compactor));
228
229
            if ('_HumbugBox' === substr($compactorClassParts[0], 0, strlen('_HumbugBox'))) {
230
                // Keep the non prefixed class name for the user
231
                array_shift($compactorClassParts);
232
            }
233
234
            $io->writeln(
235
                sprintf(
236
                    '  <comment>+</comment> %s',
237
                    implode('\\', $compactorClassParts)
238
                )
239
            );
240
        };
241
242
        array_map($logCompactors, $nestedCompactors);
243
244
        $io->newLine();
245
    }
246
247
    private function retrieveWhitelist(Compactors $compactors): ?Whitelist
248
    {
249
        foreach ($compactors->toArray() as $compactor) {
250
            if ($compactor instanceof PhpScoper) {
251
                return $compactor->getScoper()->getWhitelist();
252
            }
253
        }
254
255
        return null;
256
    }
257
258
    private function exportWhitelist(Whitelist $whitelist, OutputInterface $output): string
259
    {
260
        $cloner = new VarCloner();
261
        $cloner->setMaxItems(-1);
262
        $cloner->setMaxString(-1);
263
264
        $cliDumper = new CliDumper();
265
        if ($output->isDecorated()) {
266
            $cliDumper->setColors(true);
267
        }
268
269
        return $cliDumper->dump(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cliDumper->dump(...eVar($whitelist), true) could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
270
            $cloner->cloneVar($whitelist),
271
            true
272
        );
273
    }
274
}
275