Completed
Pull Request — master (#65)
by Michael
01:28
created

Installer::getRootPackageAlias()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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