Passed
Pull Request — master (#567)
by Théo
02:03
created

ComposerOrchestrator::getVersion()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 12
nc 3
nop 0
dl 0
loc 24
rs 9.8666
c 1
b 0
f 0
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\Composer;
16
17
use Humbug\PhpScoper\Autoload\ScoperAutoloadGenerator;
18
use Humbug\PhpScoper\Symbol\SymbolsRegistry;
19
use Humbug\PhpScoper\Whitelist;
20
use const KevinGH\Box\BOX_ALLOW_XDEBUG;
0 ignored issues
show
Bug introduced by
The constant KevinGH\Box\BOX_ALLOW_XDEBUG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
21
use KevinGH\Box\Console\IO\IO;
22
use KevinGH\Box\Console\Logger\CompilerLogger;
23
use function KevinGH\Box\FileSystem\dump_file;
24
use function KevinGH\Box\FileSystem\file_contents;
25
use KevinGH\Box\NotInstantiable;
26
use const PHP_EOL;
27
use function preg_replace;
28
use RuntimeException;
29
use function str_replace;
30
use Symfony\Component\Console\Output\OutputInterface;
31
use Symfony\Component\Process\Exception\ProcessFailedException;
32
use Symfony\Component\Process\ExecutableFinder;
33
use Symfony\Component\Process\Process;
34
use function trim;
35
36
/**
37
 * @private
38
 */
39
final class ComposerOrchestrator
40
{
41
    use NotInstantiable;
42
43
    public static function getVersion(): string
44
    {
45
        $composerExecutable = self::retrieveComposerExecutable();
46
        $command = [$composerExecutable, '--version'];
47
48
        $getVersionProcess = new Process($command);
49
50
        $getVersionProcess->run(null, self::getDefaultEnvVars());
51
52
        if (false === $getVersionProcess->isSuccessful()) {
53
            throw new RuntimeException(
54
                'Could not determine the Composer version.',
55
                0,
56
                new ProcessFailedException($getVersionProcess)
57
            );
58
        }
59
60
        $output = $getVersionProcess->getOutput();
61
62
        if (preg_match('/^Composer version ([^\\s]+)/', $output, $match) > 0) {
63
            return $match[1];
64
        }
65
66
        throw new RuntimeException('Could not determine the Composer version.');
67
    }
68
69
    public static function dumpAutoload(
70
        Whitelist $whitelist,
71
        string $prefix,
72
        bool $excludeDevFiles,
73
        IO $io = null
74
    ): void {
75
        if (null === $io) {
76
            $io = IO::createNull();
77
        }
78
79
        $logger = new CompilerLogger($io);
80
81
        $composerExecutable = self::retrieveComposerExecutable();
82
83
        self::dumpAutoloader($composerExecutable, true === $excludeDevFiles, $logger);
84
85
        if ('' !== $prefix) {
86
            $autoloadFile = self::retrieveAutoloadFile($composerExecutable, $logger);
87
88
            $autoloadContents = self::generateAutoloadStatements(
89
                $whitelist,
90
                $prefix,
91
                file_contents($autoloadFile)
92
            );
93
94
            dump_file($autoloadFile, $autoloadContents);
95
        }
96
    }
97
98
    private static function generateAutoloadStatements(Whitelist $whitelist, string $prefix, string $autoload): string
0 ignored issues
show
Unused Code introduced by
The parameter $prefix is not used and could be removed. ( Ignorable by Annotation )

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

98
    private static function generateAutoloadStatements(Whitelist $whitelist, /** @scrutinizer ignore-unused */ string $prefix, string $autoload): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
99
    {
100
        if ([] === $whitelist->toArray()) {
101
            return $autoload;
102
        }
103
104
        $autoload = str_replace('<?php', '', $autoload);
105
106
        $autoload = preg_replace(
107
            '/return (ComposerAutoloaderInit.+::getLoader\(\));/',
108
            '\$loader = $1;',
109
            $autoload
110
        );
111
112
        $whitelistStatements = (new ScoperAutoloadGenerator(SymbolsRegistry::fromWhitelist($whitelist)))->dump();
113
114
        $whitelistStatements = preg_replace(
115
            '/scoper\-autoload\.php \@generated by PhpScoper/',
116
            '@generated by Humbug Box',
117
            $whitelistStatements
118
        );
119
120
        $whitelistStatements = preg_replace(
121
            '/(\s*\\$loader \= .*)/',
122
            $autoload,
123
            $whitelistStatements
124
        );
125
126
        return preg_replace(
127
            '/\n{2,}/m',
128
            PHP_EOL.PHP_EOL,
129
            $whitelistStatements
130
        );
131
    }
132
133
    private static function retrieveComposerExecutable(): string
134
    {
135
        $executableFinder = new ExecutableFinder();
136
        $executableFinder->addSuffix('.phar');
137
138
        if (null === $composer = $executableFinder->find('composer')) {
139
            throw new RuntimeException('Could not find a Composer executable.');
140
        }
141
142
        return $composer;
143
    }
144
145
    private static function dumpAutoloader(string $composerExecutable, bool $noDev, CompilerLogger $logger): void
146
    {
147
        $composerCommand = [$composerExecutable, 'dump-autoload', '--classmap-authoritative'];
148
149
        if (true === $noDev) {
150
            $composerCommand[] = '--no-dev';
151
        }
152
153
        if (null !== $verbosity = self::retrieveSubProcessVerbosity($logger->getIO())) {
154
            $composerCommand[] = $verbosity;
155
        }
156
157
        if ($logger->getIO()->isDecorated()) {
158
            $composerCommand[] = '--ansi';
159
        }
160
161
        $dumpAutoloadProcess = new Process($composerCommand);
162
163
        $logger->log(
164
            CompilerLogger::CHEVRON_PREFIX,
165
            $dumpAutoloadProcess->getCommandLine(),
166
            OutputInterface::VERBOSITY_VERBOSE
167
        );
168
169
        $dumpAutoloadProcess->run(null, self::getDefaultEnvVars());
170
171
        if (false === $dumpAutoloadProcess->isSuccessful()) {
172
            throw new RuntimeException(
173
                'Could not dump the autoloader.',
174
                0,
175
                new ProcessFailedException($dumpAutoloadProcess)
176
            );
177
        }
178
179
        if ('' !== $output = $dumpAutoloadProcess->getOutput()) {
180
            $logger->getIO()->writeln($output, OutputInterface::VERBOSITY_VERBOSE);
181
        }
182
183
        if ('' !== $output = $dumpAutoloadProcess->getErrorOutput()) {
184
            $logger->getIO()->writeln($output, OutputInterface::VERBOSITY_VERBOSE);
185
        }
186
    }
187
188
    private static function retrieveAutoloadFile(string $composerExecutable, CompilerLogger $logger): string
189
    {
190
        $command = [$composerExecutable, 'config', 'vendor-dir'];
191
192
        if ($logger->getIO()->isDecorated()) {
193
            $command[] = '--ansi';
194
        }
195
196
        $vendorDirProcess = new Process($command);
197
198
        $logger->log(
199
            CompilerLogger::CHEVRON_PREFIX,
200
            $vendorDirProcess->getCommandLine(),
201
            OutputInterface::VERBOSITY_VERBOSE
202
        );
203
204
        $vendorDirProcess->run(null, self::getDefaultEnvVars());
205
206
        if (false === $vendorDirProcess->isSuccessful()) {
207
            throw new RuntimeException(
208
                'Could not retrieve the vendor dir.',
209
                0,
210
                new ProcessFailedException($vendorDirProcess)
211
            );
212
        }
213
214
        return trim($vendorDirProcess->getOutput()).'/autoload.php';
215
    }
216
217
    private static function retrieveSubProcessVerbosity(IO $io): ?string
218
    {
219
        if ($io->isDebug()) {
220
            return '-vvv';
221
        }
222
223
        if ($io->isVeryVerbose()) {
224
            return '-v';
225
        }
226
227
        return null;
228
    }
229
230
    private static function getDefaultEnvVars(): array
231
    {
232
        $vars = [];
233
234
        if ('1' === (string) getenv(BOX_ALLOW_XDEBUG)) {
235
            $vars['COMPOSER_ALLOW_XDEBUG'] = '1';
236
        }
237
238
        return $vars;
239
    }
240
}
241