Passed
Push — master ( e708d7...ee2e9a )
by Théo
03:48
created

ComposerOrchestrator::getVersion()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 24
rs 9.8666
cc 3
nc 3
nop 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\Whitelist;
19
use KevinGH\Box\Console\IO\IO;
20
use KevinGH\Box\Console\Logger\CompilerLogger;
21
use function KevinGH\Box\FileSystem\dump_file;
22
use function KevinGH\Box\FileSystem\file_contents;
23
use KevinGH\Box\NotInstantiable;
24
use const PHP_EOL;
25
use function preg_replace;
26
use RuntimeException;
27
use function str_replace;
28
use Symfony\Component\Console\Output\OutputInterface;
29
use Symfony\Component\Process\Exception\ProcessFailedException;
30
use Symfony\Component\Process\ExecutableFinder;
31
use Symfony\Component\Process\Process;
32
use function trim;
33
34
/**
35
 * @private
36
 */
37
final class ComposerOrchestrator
38
{
39
    use NotInstantiable;
40
41
    public static function getVersion(): string
42
    {
43
        $composerExecutable = self::retrieveComposerExecutable();
44
        $command = [$composerExecutable, '--version'];
45
46
        $getVersionProcess = new Process($command);
47
48
        $getVersionProcess->run();
49
50
        if (false === $getVersionProcess->isSuccessful()) {
51
            throw new RuntimeException(
52
                'Could not determine the Composer version.',
53
                0,
54
                new ProcessFailedException($getVersionProcess)
55
            );
56
        }
57
58
        $output = $getVersionProcess->getOutput();
59
60
        if (preg_match('/^Composer version ([^\\s]+)/', $output, $match) > 0) {
61
            return $match[1];
62
        }
63
64
        throw new RuntimeException('Could not determine the Composer version.');
65
    }
66
67
    public static function dumpAutoload(
68
        Whitelist $whitelist,
69
        string $prefix,
70
        bool $excludeDevFiles,
71
        IO $io = null
72
    ): void {
73
        if (null === $io) {
74
            $io = IO::createNull();
75
        }
76
77
        $logger = new CompilerLogger($io);
78
79
        $composerExecutable = self::retrieveComposerExecutable();
80
81
        self::dumpAutoloader($composerExecutable, true === $excludeDevFiles, $logger);
82
83
        if ('' !== $prefix) {
84
            $autoloadFile = self::retrieveAutoloadFile($composerExecutable, $logger);
85
86
            $autoloadContents = self::generateAutoloadStatements(
87
                $whitelist,
88
                $prefix,
89
                file_contents($autoloadFile)
90
            );
91
92
            dump_file($autoloadFile, $autoloadContents);
93
        }
94
    }
95
96
    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

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