Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 83 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
DumpAutoload | |
0.00% |
0 / 83 |
|
0.00% |
0 / 4 |
56 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
generatedPrefixedAutoloader | |
0.00% |
0 / 43 |
|
0.00% |
0 / 1 |
12 | |||
prefixNewAutoloader | |
0.00% |
0 / 34 |
|
0.00% |
0 / 1 |
2 | |||
getSuffix | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | namespace BrianHenryIE\Strauss\Pipeline\Autoload; |
4 | |
5 | use BrianHenryIE\Strauss\Files\File; |
6 | use BrianHenryIE\Strauss\Config\AutoloadConfigInterface; |
7 | use BrianHenryIE\Strauss\Helpers\FileSystem; |
8 | use BrianHenryIE\Strauss\Pipeline\FileEnumerator; |
9 | use BrianHenryIE\Strauss\Pipeline\Prefixer; |
10 | use BrianHenryIE\Strauss\Types\DiscoveredSymbols; |
11 | use BrianHenryIE\Strauss\Types\NamespaceSymbol; |
12 | use Composer\Autoload\AutoloadGenerator; |
13 | use Composer\Config; |
14 | use Composer\Factory; |
15 | use Composer\IO\NullIO; |
16 | use Composer\Json\JsonFile; |
17 | use Composer\Repository\InstalledFilesystemRepository; |
18 | use Psr\Log\LoggerAwareTrait; |
19 | use Psr\Log\LoggerInterface; |
20 | use Psr\Log\NullLogger; |
21 | |
22 | class DumpAutoload |
23 | { |
24 | use LoggerAwareTrait; |
25 | |
26 | protected AutoloadConfigInterface $config; |
27 | |
28 | protected FileSystem $filesystem; |
29 | |
30 | public function __construct( |
31 | AutoloadConfigInterface $config, |
32 | Filesystem $filesystem, |
33 | ?LoggerInterface $logger = null |
34 | ) { |
35 | $this->config = $config; |
36 | $this->filesystem = $filesystem; |
37 | $this->setLogger($logger ?? new NullLogger()); |
38 | } |
39 | |
40 | /** |
41 | * Uses `vendor/composer/installed.json` to output autoload files to `vendor-prefixed/composer`. |
42 | */ |
43 | public function generatedPrefixedAutoloader(): void |
44 | { |
45 | /** |
46 | * Unfortunately, `::dump()` creates the target directories if they don't exist, even though it otherwise respects `::setDryRun()`. |
47 | */ |
48 | if ($this->config->isDryRun()) { |
49 | return; |
50 | } |
51 | |
52 | $relativeTargetDir = $this->filesystem->getRelativePath( |
53 | $this->config->getProjectDirectory(), |
54 | $this->config->getTargetDirectory() |
55 | ); |
56 | |
57 | $defaultVendorDirBefore = Config::$defaultConfig['vendor-dir']; |
58 | |
59 | Config::$defaultConfig['vendor-dir'] = $relativeTargetDir; |
60 | |
61 | $composer = Factory::create(new NullIO(), $this->config->getProjectDirectory() . 'composer.json'); |
62 | $installationManager = $composer->getInstallationManager(); |
63 | $package = $composer->getPackage(); |
64 | |
65 | $projectComposerJson = new JsonFile($this->config->getProjectDirectory() . 'composer.json'); |
66 | $projectComposerJsonArray = $projectComposerJson->read(); |
67 | if (isset($projectComposerJsonArray['config'], $projectComposerJsonArray['config']['vendor-dir'])) { |
68 | unset($projectComposerJsonArray['config']['vendor-dir']); |
69 | } |
70 | |
71 | /** |
72 | * Cannot use `$composer->getConfig()`, need to create a new one so the vendor-dir is correct. |
73 | */ |
74 | $config = new \Composer\Config(false, $this->config->getProjectDirectory()); |
75 | |
76 | $config->merge([ |
77 | 'config' => $projectComposerJsonArray['config'] ?? [] |
78 | ]); |
79 | |
80 | $generator = new ComposerAutoloadGenerator( |
81 | $this->config->getNamespacePrefix(), |
82 | $composer->getEventDispatcher() |
83 | ); |
84 | |
85 | $generator->setDryRun($this->config->isDryRun()); |
86 | $generator->setClassMapAuthoritative(true); |
87 | $generator->setRunScripts(false); |
88 | // $generator->setApcu($apcu, $apcuPrefix); |
89 | // $generator->setPlatformRequirementFilter($this->getPlatformRequirementFilter($input)); |
90 | $optimize = true; // $input->getOption('optimize') || $config->get('optimize-autoloader'); |
91 | $generator->setDevMode(false); |
92 | |
93 | $localRepo = new InstalledFilesystemRepository(new JsonFile($this->config->getTargetDirectory() . 'composer/installed.json')); |
94 | |
95 | $strictAmbiguous = false; // $input->getOption('strict-ambiguous') |
96 | |
97 | // This will output the autoload_static.php etc. files to `vendor-prefixed/composer`. |
98 | $generator->dump( |
99 | $config, |
100 | $localRepo, |
101 | $package, |
102 | $installationManager, |
103 | 'composer', |
104 | $optimize, |
105 | $this->getSuffix(), |
106 | $composer->getLocker(), |
107 | $strictAmbiguous |
108 | ); |
109 | |
110 | /** |
111 | * Tests fail if this is absent. |
112 | * |
113 | * Arguably this should be in ::setUp() and tearDown() in the test classes, but if other tools run after Strauss |
114 | * then they might expect it to be unmodified. |
115 | */ |
116 | Config::$defaultConfig['vendor-dir'] = $defaultVendorDirBefore; |
117 | |
118 | $this->prefixNewAutoloader(); |
119 | } |
120 | |
121 | protected function prefixNewAutoloader(): void |
122 | { |
123 | $this->logger->debug('Prefixing the new Composer autoloader.'); |
124 | |
125 | $projectReplace = new Prefixer( |
126 | $this->config, |
127 | $this->filesystem, |
128 | $this->logger |
129 | ); |
130 | |
131 | $fileEnumerator = new FileEnumerator( |
132 | $this->config, |
133 | $this->filesystem |
134 | ); |
135 | |
136 | $projectFiles = $fileEnumerator->compileFileListForPaths([ |
137 | $this->config->getTargetDirectory() . 'composer', |
138 | ]); |
139 | |
140 | $phpFiles = array_filter( |
141 | $projectFiles->getFiles(), |
142 | fn($file) => $file->isPhpFile() |
143 | ); |
144 | |
145 | $phpFilesAbsolutePaths = array_map( |
146 | fn($file) => $file->getSourcePath(), |
147 | $phpFiles |
148 | ); |
149 | |
150 | $sourceFile = new File(__DIR__); |
151 | $composerNamespaceSymbol = new NamespaceSymbol( |
152 | 'Composer\\Autoload', |
153 | $sourceFile |
154 | ); |
155 | $composerNamespaceSymbol->setReplacement( |
156 | $this->config->getNamespacePrefix() . '\\Composer\\Autoload' |
157 | ); |
158 | |
159 | $discoveredSymbols = new DiscoveredSymbols(); |
160 | $discoveredSymbols->add( |
161 | $composerNamespaceSymbol |
162 | ); |
163 | |
164 | $projectReplace->replaceInProjectFiles($discoveredSymbols, $phpFilesAbsolutePaths); |
165 | } |
166 | |
167 | /** |
168 | * If there is an existing autoloader, it will use the same suffix. If there is not, it pulls the suffix from |
169 | * {Composer::getLocker()} and clashes with the existing autoloader. |
170 | * |
171 | * @see AutoloadGenerator::dump() 412:431 |
172 | * @see https://github.com/composer/composer/blob/ae208dc1e182bd45d99fcecb956501da212454a1/src/Composer/Autoload/AutoloadGenerator.php#L429 |
173 | */ |
174 | protected function getSuffix(): ?string |
175 | { |
176 | return !$this->filesystem->fileExists($this->config->getTargetDirectory() . 'autoload.php') |
177 | ? bin2hex(random_bytes(16)) |
178 | : null; |
179 | } |
180 | } |