Passed
Pull Request — master (#389)
by Théo
02:38
created

Process::retrieveWhitelist()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 9
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 1
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
                    'Whitelist:',
157
                    '',
158
                    '<comment>"""</comment>',
159
                    $this->exportWhitelist($whitelist, $output),
160
                    '<comment>"""</comment>',
161
                ]);
162
            }
163
        }
164
    }
165
166
    private function retrieveCompactors(Configuration $config): Compactors
167
    {
168
        $compactors = $config->getCompactors();
169
170
        array_unshift(
171
            $compactors,
172
            new Placeholder($config->getReplacements())
173
        );
174
175
        return new Compactors(...$compactors);
176
    }
177
178
    private function logPlaceholders(SymfonyStyle $io, Configuration $config): void
179
    {
180
        if ([] === $config->getReplacements()) {
181
            $io->writeln([
182
                'No replacement values registered',
183
                '',
184
            ]);
185
186
            return;
187
        }
188
189
        $io->writeln('Registered replacement values:');
190
191
        foreach ($config->getReplacements() as $key => $value) {
192
            $io->writeln(
193
                sprintf(
194
                    '  <comment>+</comment> %s: %s',
195
                    $key,
196
                    $value
197
                )
198
            );
199
        }
200
201
        $io->newLine();
202
    }
203
204
    private function logCompactors(SymfonyStyle $io, Compactors $compactors): void
205
    {
206
        $nestedCompactors = $compactors->toArray();
207
208
        foreach ($nestedCompactors as $index => $compactor) {
209
            if ($compactor instanceof Placeholder) {
210
                unset($nestedCompactors[$index]);
211
            }
212
        }
213
214
        if ([] === $nestedCompactors) {
215
            $io->writeln([
216
                'No compactor registered',
217
                '',
218
            ]);
219
220
            return;
221
        }
222
223
        $io->writeln('Registered compactors:');
224
225
        $logCompactors = static function (Compactor $compactor) use ($io): void {
226
            $compactorClassParts = explode('\\', get_class($compactor));
227
228
            if ('_HumbugBox' === substr($compactorClassParts[0], 0, strlen('_HumbugBox'))) {
229
                // Keep the non prefixed class name for the user
230
                array_shift($compactorClassParts);
231
            }
232
233
            $io->writeln(
234
                sprintf(
235
                    '  <comment>+</comment> %s',
236
                    implode('\\', $compactorClassParts)
237
                )
238
            );
239
        };
240
241
        array_map($logCompactors, $nestedCompactors);
242
243
        $io->newLine();
244
    }
245
246
    private function retrieveWhitelist(Compactors $compactors): ?Whitelist
247
    {
248
        foreach ($compactors->toArray() as $compactor) {
249
            if ($compactor instanceof PhpScoper) {
250
                return $compactor->getScoper()->getWhitelist();
251
            }
252
        }
253
254
        return null;
255
    }
256
257
    private function exportWhitelist(Whitelist $whitelist, OutputInterface $output): string
258
    {
259
        $cloner = new VarCloner();
260
        $cloner->setMaxItems(-1);
261
        $cloner->setMaxString(-1);
262
263
        $cliDumper = new CliDumper();
264
        if ($output->isDecorated()) {
265
            $cliDumper->setColors(true);
266
        }
267
268
        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...
269
            $cloner->cloneVar($whitelist),
270
            true
271
        );
272
    }
273
}
274