1 | <?php |
||
32 | final class Installer implements PluginInterface, EventSubscriberInterface |
||
33 | { |
||
34 | private static string $generatedClassTemplate = <<<'PHP' |
||
|
|||
35 | <?php |
||
36 | |||
37 | declare(strict_types=1); |
||
38 | |||
39 | namespace PackageVersions; |
||
40 | |||
41 | use OutOfBoundsException; |
||
42 | |||
43 | /** |
||
44 | * This class is generated by ocramius/package-versions, specifically by |
||
45 | * @see \PackageVersions\Installer |
||
46 | * |
||
47 | * This file is overwritten at every run of `composer install` or `composer update`. |
||
48 | */ |
||
49 | %s |
||
50 | { |
||
51 | public const ROOT_PACKAGE_NAME = '%s'; |
||
52 | /** |
||
53 | * Array of all available composer packages. |
||
54 | * Dont read this array from your calling code, but use the \PackageVersions\Versions::getVersion() method instead. |
||
55 | * |
||
56 | * @var array<string, string> |
||
57 | * @internal |
||
58 | */ |
||
59 | public const VERSIONS = %s; |
||
60 | |||
61 | private function __construct() |
||
62 | { |
||
63 | } |
||
64 | |||
65 | /** |
||
66 | * @throws OutOfBoundsException If a version cannot be located. |
||
67 | * |
||
68 | * @psalm-param key-of<self::VERSIONS> $packageName |
||
69 | */ |
||
70 | public static function getVersion(string $packageName) : string |
||
71 | { |
||
72 | if (isset(self::VERSIONS[$packageName])) { |
||
73 | return self::VERSIONS[$packageName]; |
||
74 | } |
||
75 | |||
76 | throw new OutOfBoundsException( |
||
77 | 'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files' |
||
78 | ); |
||
79 | } |
||
80 | } |
||
81 | |||
82 | PHP; |
||
83 | |||
84 | public function activate(Composer $composer, IOInterface $io) : void |
||
85 | { |
||
86 | // Nothing to do here, as all features are provided through event listeners |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * {@inheritDoc} |
||
91 | */ |
||
92 | 1 | public static function getSubscribedEvents() : array |
|
93 | { |
||
94 | 1 | return [ScriptEvents::POST_AUTOLOAD_DUMP => 'dumpVersionsClass']; |
|
95 | } |
||
96 | |||
97 | /** |
||
98 | * @throws RuntimeException |
||
99 | */ |
||
100 | 14 | public static function dumpVersionsClass(Event $composerEvent) : void |
|
101 | { |
||
102 | 14 | $composer = $composerEvent->getComposer(); |
|
103 | 14 | $rootPackage = $composer->getPackage(); |
|
104 | 14 | $versions = iterator_to_array(self::getVersions($composer->getLocker(), $rootPackage)); |
|
105 | |||
106 | 14 | if (! array_key_exists('ocramius/package-versions', $versions)) { |
|
107 | //plugin must be globally installed - we only want to generate versions for projects which specifically |
||
108 | //require ocramius/package-versions |
||
109 | 1 | return; |
|
110 | } |
||
111 | |||
112 | 13 | $versionClass = self::generateVersionsClass($rootPackage->getName(), $versions); |
|
113 | |||
114 | 13 | self::writeVersionClassToFile($versionClass, $composer, $composerEvent->getIO()); |
|
115 | 13 | } |
|
116 | |||
117 | /** |
||
118 | * @param string[] $versions |
||
119 | */ |
||
120 | 13 | private static function generateVersionsClass(string $rootPackageName, array $versions) : string |
|
121 | { |
||
122 | 13 | return sprintf( |
|
123 | 13 | self::$generatedClassTemplate, |
|
124 | 13 | 'fin' . 'al ' . 'cla' . 'ss ' . 'Versions', // note: workaround for regex-based code parsers :-( |
|
125 | 13 | $rootPackageName, |
|
126 | 13 | var_export($versions, true) |
|
127 | ); |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * @throws RuntimeException |
||
132 | */ |
||
133 | 13 | private static function writeVersionClassToFile(string $versionClassSource, Composer $composer, IOInterface $io) : void |
|
134 | { |
||
135 | 13 | $installPath = self::locateRootPackageInstallPath($composer->getConfig(), $composer->getPackage()) |
|
136 | 13 | . '/src/PackageVersions/Versions.php'; |
|
137 | |||
138 | 13 | if (! file_exists(dirname($installPath))) { |
|
139 | 1 | $io->write('<info>ocramius/package-versions:</info> Package not found (probably scheduled for removal); generation of version class skipped.'); |
|
140 | |||
141 | 1 | return; |
|
142 | } |
||
143 | |||
144 | 12 | $io->write('<info>ocramius/package-versions:</info> Generating version class...'); |
|
145 | |||
146 | 12 | $installPathTmp = $installPath . '_' . uniqid('tmp', true); |
|
147 | 12 | file_put_contents($installPathTmp, $versionClassSource); |
|
148 | 12 | chmod($installPathTmp, 0664); |
|
149 | 12 | rename($installPathTmp, $installPath); |
|
150 | |||
151 | 12 | $io->write('<info>ocramius/package-versions:</info> ...done generating version class'); |
|
152 | 12 | } |
|
153 | |||
154 | /** |
||
155 | * @throws RuntimeException |
||
156 | */ |
||
157 | 13 | private static function locateRootPackageInstallPath( |
|
158 | Config $composerConfig, |
||
159 | RootPackageInterface $rootPackage |
||
160 | ) : string { |
||
161 | 13 | if (self::getRootPackageAlias($rootPackage)->getName() === 'ocramius/package-versions') { |
|
162 | 3 | return dirname($composerConfig->get('vendor-dir')); |
|
163 | } |
||
164 | |||
165 | 10 | return $composerConfig->get('vendor-dir') . '/ocramius/package-versions'; |
|
166 | } |
||
167 | |||
168 | 13 | private static function getRootPackageAlias(RootPackageInterface $rootPackage) : PackageInterface |
|
169 | { |
||
170 | 13 | $package = $rootPackage; |
|
171 | |||
172 | 13 | while ($package instanceof AliasPackage) { |
|
173 | 4 | $package = $package->getAliasOf(); |
|
174 | } |
||
175 | |||
176 | 13 | return $package; |
|
177 | } |
||
178 | |||
179 | /** |
||
180 | * @return Generator&string[] |
||
181 | * |
||
182 | * @psalm-return Generator<string, string> |
||
183 | */ |
||
184 | 14 | private static function getVersions(Locker $locker, RootPackageInterface $rootPackage) : Generator |
|
185 | { |
||
186 | 14 | $lockData = $locker->getLockData(); |
|
187 | |||
188 | 14 | $lockData['packages-dev'] = $lockData['packages-dev'] ?? []; |
|
189 | |||
190 | 14 | foreach (array_merge($lockData['packages'], $lockData['packages-dev']) as $package) { |
|
191 | 14 | yield $package['name'] => $package['version'] . '@' . ( |
|
192 | 14 | $package['source']['reference']?? $package['dist']['reference'] ?? '' |
|
193 | ); |
||
194 | } |
||
195 | |||
196 | 14 | foreach ($rootPackage->getReplaces() as $replace) { |
|
197 | 5 | $version = $replace->getPrettyConstraint(); |
|
198 | 5 | if ($version === 'self.version') { |
|
199 | 5 | $version = $rootPackage->getPrettyVersion(); |
|
200 | } |
||
201 | |||
202 | 5 | yield $replace->getTarget() => $version . '@' . $rootPackage->getSourceReference(); |
|
203 | } |
||
204 | |||
205 | 14 | yield $rootPackage->getName() => $rootPackage->getPrettyVersion() . '@' . $rootPackage->getSourceReference(); |
|
206 | 14 | } |
|
207 | } |
||
208 |