Issues (224)

src/Console/Command/Process.php (1 issue)

Labels
Severity
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 Fidry\Console\Command\Command;
18
use Fidry\Console\Command\Configuration as ConsoleConfiguration;
19
use Fidry\Console\ExitCode;
20
use Fidry\Console\IO;
0 ignored issues
show
The type Fidry\Console\IO was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use Fidry\FileSystem\FS;
22
use Humbug\PhpScoper\Symbol\SymbolsRegistry;
23
use KevinGH\Box\Compactor\Compactor;
24
use KevinGH\Box\Compactor\Compactors;
25
use KevinGH\Box\Compactor\PhpScoper;
26
use KevinGH\Box\Compactor\Placeholder;
27
use KevinGH\Box\Configuration\Configuration;
28
use KevinGH\Box\Console\Php\PhpSettingsChecker;
29
use KevinGH\Box\Constants;
30
use stdClass;
31
use Symfony\Component\Console\Input\InputArgument;
32
use Symfony\Component\Console\Input\InputOption;
33
use Symfony\Component\Console\Output\OutputInterface;
34
use Symfony\Component\Filesystem\Path;
35
use Symfony\Component\VarDumper\Cloner\VarCloner;
36
use Symfony\Component\VarDumper\Dumper\CliDumper;
37
use function array_map;
38
use function array_shift;
39
use function array_unshift;
40
use function explode;
41
use function getcwd;
42
use function implode;
43
use function putenv;
44
use function sprintf;
45
46
// TODO: replace the PHP-Scoper compactor in order to warn the user about scoping errors
47
final class Process implements Command
48
{
49
    private const FILE_ARGUMENT = 'file';
50
51
    private const NO_RESTART_OPTION = 'no-restart';
52
    private const NO_CONFIG_OPTION = 'no-config';
53
54
    public function getConfiguration(): ConsoleConfiguration
55
    {
56
        return new ConsoleConfiguration(
57
            'process',
58
            '⚡  Applies the registered compactors and replacement values on a file',
59
            'The <info>%command.name%</info> command will apply the registered compactors and replacement values on the the given file. This is useful to debug the scoping of a specific file for example.',
60
            [
61
                new InputArgument(
62
                    self::FILE_ARGUMENT,
63
                    InputArgument::REQUIRED,
64
                    'Path to the file to process',
65
                ),
66
            ],
67
            [
68
                new InputOption(
69
                    self::NO_RESTART_OPTION,
70
                    null,
71
                    InputOption::VALUE_NONE,
72
                    'Do not restart the PHP process. Box restarts the process by default to disable xdebug',
73
                ),
74
                new InputOption(
75
                    self::NO_CONFIG_OPTION,
76
                    null,
77
                    InputOption::VALUE_NONE,
78
                    'Ignore the config file even when one is specified with the --config option',
79
                ),
80
                ConfigOption::getOptionInput(),
81
                ChangeWorkingDirOption::getOptionInput(),
82
            ],
83
        );
84
    }
85
86
    public function execute(IO $io): int
87
    {
88
        if ($io->getTypedOption(self::NO_RESTART_OPTION)->asBoolean()) {
89
            putenv(Constants::ALLOW_XDEBUG.'=1');
90
        }
91
92
        PhpSettingsChecker::check($io);
93
94
        ChangeWorkingDirOption::changeWorkingDirectory($io);
95
96
        $io->newLine();
97
98
        $config = $io->getTypedOption(self::NO_CONFIG_OPTION)->asBoolean()
99
            ? Configuration::create(null, new stdClass())
100
            : ConfigOption::getConfig($io, true);
101
102
        $filePath = $io->getTypedArgument(self::FILE_ARGUMENT)->asNonEmptyString();
103
104
        $path = Path::makeRelative($filePath, $config->getBasePath());
105
106
        $compactors = self::retrieveCompactors($config);
107
108
        $fileContents = FS::getFileContents(
109
            $absoluteFilePath = Path::makeAbsolute(
110
                $filePath,
111
                getcwd(),
112
            ),
113
        );
114
115
        $io->writeln([
116
            sprintf(
117
                '⚡  Processing the contents of the file <info>%s</info>',
118
                $absoluteFilePath,
119
            ),
120
            '',
121
        ]);
122
123
        self::logPlaceholders($config, $io);
124
        self::logCompactors($compactors, $io);
125
126
        $fileProcessedContents = $compactors->compact($path, $fileContents);
127
128
        if ($io->isQuiet()) {
129
            $io->writeln($fileProcessedContents, OutputInterface::VERBOSITY_QUIET);
130
        } else {
131
            $symbolsRegistry = self::retrieveSymbolsRegistry($compactors);
132
133
            $io->writeln([
134
                'Processed contents:',
135
                '',
136
                '<comment>"""</comment>',
137
                $fileProcessedContents,
138
                '<comment>"""</comment>',
139
            ]);
140
141
            if (null !== $symbolsRegistry) {
142
                $io->writeln([
143
                    '',
144
                    'Symbols Registry:',
145
                    '',
146
                    '<comment>"""</comment>',
147
                    self::exportSymbolsRegistry($symbolsRegistry, $io),
148
                    '<comment>"""</comment>',
149
                ]);
150
            }
151
        }
152
153
        return ExitCode::SUCCESS;
154
    }
155
156
    private static function retrieveCompactors(Configuration $config): Compactors
157
    {
158
        $compactors = $config->getCompactors()->toArray();
159
160
        array_unshift(
161
            $compactors,
162
            new Placeholder($config->getReplacements()),
163
        );
164
165
        return new Compactors(...$compactors);
166
    }
167
168
    private static function logPlaceholders(Configuration $config, IO $io): void
169
    {
170
        if (0 === count($config->getReplacements())) {
171
            $io->writeln([
172
                'No replacement values registered',
173
                '',
174
            ]);
175
176
            return;
177
        }
178
179
        $io->writeln('Registered replacement values:');
180
181
        foreach ($config->getReplacements() as $key => $value) {
182
            $io->writeln(
183
                sprintf(
184
                    '  <comment>+</comment> %s: %s',
185
                    $key,
186
                    $value,
187
                ),
188
            );
189
        }
190
191
        $io->newLine();
192
    }
193
194
    private static function logCompactors(Compactors $compactors, IO $io): void
195
    {
196
        $nestedCompactors = $compactors->toArray();
197
198
        foreach ($nestedCompactors as $index => $compactor) {
199
            if ($compactor instanceof Placeholder) {
200
                unset($nestedCompactors[$index]);
201
            }
202
        }
203
204
        if ([] === $nestedCompactors) {
205
            $io->writeln([
206
                'No compactor registered',
207
                '',
208
            ]);
209
210
            return;
211
        }
212
213
        $io->writeln('Registered compactors:');
214
215
        $logCompactors = static function (Compactor $compactor) use ($io): void {
216
            $compactorClassParts = explode('\\', $compactor::class);
217
218
            if (str_starts_with($compactorClassParts[0], '_HumbugBox')) {
219
                // Keep the non prefixed class name for the user
220
                array_shift($compactorClassParts);
221
            }
222
223
            $io->writeln(
224
                sprintf(
225
                    '  <comment>+</comment> %s',
226
                    implode('\\', $compactorClassParts),
227
                ),
228
            );
229
        };
230
231
        array_map($logCompactors, $nestedCompactors);
232
233
        $io->newLine();
234
    }
235
236
    private static function retrieveSymbolsRegistry(Compactors $compactors): ?SymbolsRegistry
237
    {
238
        foreach ($compactors->toArray() as $compactor) {
239
            if ($compactor instanceof PhpScoper) {
240
                return $compactor->getScoper()->getSymbolsRegistry();
241
            }
242
        }
243
244
        return null;
245
    }
246
247
    private static function exportSymbolsRegistry(SymbolsRegistry $symbolsRegistry, IO $io): string
248
    {
249
        $cloner = new VarCloner();
250
        $cloner->setMaxItems(-1);
251
        $cloner->setMaxString(-1);
252
253
        $cliDumper = new CliDumper();
254
        if ($io->isDecorated()) {
255
            $cliDumper->setColors(true);
256
        }
257
258
        return (string) $cliDumper->dump(
259
            $cloner->cloneVar($symbolsRegistry),
260
            true,
261
        );
262
    }
263
}
264