Completed
Pull Request — master (#79)
by
unknown
02:08
created

Installer::activate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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