Passed
Pull Request — master (#400)
by Théo
03:15
created

Process::retrieveCompactors()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 1
nc 1
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 function KevinGH\Box\check_php_settings;
27
use KevinGH\Box\Compactor\Compactor;
28
use KevinGH\Box\Compactor\Compactors;
29
use KevinGH\Box\Compactor\PhpScoper;
30
use KevinGH\Box\Compactor\Placeholder;
31
use KevinGH\Box\Configuration\Configuration;
32
use KevinGH\Box\Console\IO\IO;
33
use KevinGH\Box\Console\Php\PhpSettingsHandler;
34
use function KevinGH\Box\FileSystem\file_contents;
35
use function KevinGH\Box\FileSystem\make_path_absolute;
36
use function KevinGH\Box\FileSystem\make_path_relative;
37
use function putenv;
38
use function sprintf;
39
use stdClass;
40
use Symfony\Component\Console\Input\InputArgument;
41
use Symfony\Component\Console\Input\InputOption;
42
use Symfony\Component\Console\Logger\ConsoleLogger;
43
use Symfony\Component\Console\Output\OutputInterface;
44
use Symfony\Component\VarDumper\Cloner\VarCloner;
45
use Symfony\Component\VarDumper\Dumper\CliDumper;
46
47
final class Process extends ConfigurableBaseCommand
48
{
49
    use ChangeableWorkingDirectory;
50
51
    private const FILE_ARGUMENT = 'file';
52
53
    private const NO_RESTART_OPTION = 'no-restart';
54
    private const NO_CONFIG_OPTION = 'no-config';
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    protected function configure(): void
60
    {
61
        parent::configure();
62
63
        $this->setName('process');
64
        $this->setDescription('⚡  Applies the registered compactors and replacement values on a file');
65
        $this->setHelp(
66
            'The <info>%command.name%</info> command will apply the registered compactors and replacement values '
67
            .'on the the given file. This is useful to debug the scoping of a specific file for example.'
68
        );
69
70
        $this->addArgument(
71
            self::FILE_ARGUMENT,
72
            InputArgument::REQUIRED,
73
            'Path to the file to process'
74
        );
75
        $this->addOption(
76
            self::NO_RESTART_OPTION,
77
            null,
78
            InputOption::VALUE_NONE,
79
            'Do not restart the PHP process. Box restarts the process by default to disable xdebug'
80
        );
81
        $this->addOption(
82
            self::NO_CONFIG_OPTION,
83
            null,
84
            InputOption::VALUE_NONE,
85
            'Ignore the config file even when one is specified with the --config option'
86
        );
87
88
        $this->configureWorkingDirOption();
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94
    protected function executeCommand(IO $io): int
95
    {
96
        $input = $io->getInput();
97
98
        if ($input->getOption(self::NO_RESTART_OPTION)) {
99
            putenv(BOX_ALLOW_XDEBUG.'=1');
100
        }
101
102
        check_php_settings($io);
103
104
        $this->changeWorkingDirectory($input);
105
106
        $io->newLine();
107
108
        $config = $input->getOption(self::NO_CONFIG_OPTION)
109
            ? Configuration::create(null, new stdClass())
110
            : $this->getConfig($io, true)
111
        ;
112
113
        $filePath = $input->getArgument(self::FILE_ARGUMENT);
114
115
        $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

115
        $path = make_path_relative(/** @scrutinizer ignore-type */ $filePath, $config->getBasePath());
Loading history...
116
117
        $compactors = $this->retrieveCompactors($config);
118
119
        $fileContents = file_contents(
120
            $absoluteFilePath = make_path_absolute(
121
                $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

121
                /** @scrutinizer ignore-type */ $filePath,
Loading history...
122
                getcwd()
123
            )
124
        );
125
126
        $io->writeln([
127
            sprintf(
128
                '⚡  Processing the contents of the file <info>%s</info>',
129
                $absoluteFilePath
130
            ),
131
            '',
132
        ]);
133
134
        $this->logPlaceholders($config, $io);
135
        $this->logCompactors($compactors, $io);
136
137
        $fileProcessedContents = $compactors->compactContents($path, $fileContents);
138
139
        if ($io->isQuiet()) {
140
            $io->writeln($fileProcessedContents, OutputInterface::VERBOSITY_QUIET);
141
        } else {
142
            $whitelist = $this->retrieveWhitelist($compactors);
143
144
            $io->writeln([
145
                'Processed contents:',
146
                '',
147
                '<comment>"""</comment>',
148
                $fileProcessedContents,
149
                '<comment>"""</comment>',
150
            ]);
151
152
            if (null !== $whitelist) {
153
                $io->writeln([
154
                    '',
155
                    'Whitelist:',
156
                    '',
157
                    '<comment>"""</comment>',
158
                    $this->exportWhitelist($whitelist, $io),
159
                    '<comment>"""</comment>',
160
                ]);
161
            }
162
        }
163
164
        return 0;
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(Configuration $config, IO $io): 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(Compactors $compactors, IO $io): 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 (0 === strpos($compactorClassParts[0], '_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, IO $io): string
259
    {
260
        $cloner = new VarCloner();
261
        $cloner->setMaxItems(-1);
262
        $cloner->setMaxString(-1);
263
264
        $cliDumper = new CliDumper();
265
        if ($io->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