Passed
Push — master ( 651c37...0a0430 )
by Marco
03:03 queued 15s
created

Installer::uninstall()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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