Completed
Push — master ( e1c506...a4d4b6 )
by Marco
10s
created

Installer::writeVersionClassToFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2

Importance

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