Completed
Pull Request — master (#82)
by
unknown
02:01
created

FallbackVersions   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 72
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Test Coverage

Coverage 88.46%

Importance

Changes 0
Metric Value
wmc 9
lcom 0
cbo 0
dl 0
loc 72
ccs 23
cts 26
cp 0.8846
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A getVersion() 0 12 2
A getComposerLockPath() 0 25 3
A getVersions() 0 17 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PackageVersions;
6
7
use Generator;
8
use OutOfBoundsException;
9
use UnexpectedValueException;
10
use function array_key_exists;
11
use function array_merge;
12
use function file_exists;
13
use function file_get_contents;
14
use function getcwd;
15
use function iterator_to_array;
16
use function json_decode;
17
use function json_encode;
18
use function sprintf;
19
20
/**
21
 * @internal
22
 *
23
 * This is a fallback for {@see \PackageVersions\Versions::getVersion()}
24
 * Do not use this class directly: it is intended to be only used when
25
 * {@see \PackageVersions\Versions} fails to be generated, which typically
26
 * happens when running composer with `--no-scripts` flag)
27
 */
28
final class FallbackVersions
29
{
30
    public const ROOT_PACKAGE_NAME = 'unknown/root-package@UNKNOWN';
31
32
    private function __construct()
33
    {
34
    }
35
36
    /**
37
     * @throws OutOfBoundsException If a version cannot be located.
38
     * @throws UnexpectedValueException If the composer.lock file could not be located.
39
     */
40 4
    public static function getVersion(string $packageName) : string
41
    {
42 4
        $versions = iterator_to_array(self::getVersions(self::getComposerLockPath()));
43
44 3
        if (! array_key_exists($packageName, $versions)) {
45 1
            throw new OutOfBoundsException(
46 1
                'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files'
47
            );
48
        }
49
50 2
        return $versions[$packageName];
51
    }
52
53
    /**
54
     * @throws UnexpectedValueException
55
     */
56 4
    private static function getComposerLockPath() : string
57
    {
58
        $checkedPaths = [
59
            // The top-level project's ./vendor/composer/installed.json
60 4
            getcwd() . '/vendor/composer/installed.json',
61
            // The top-level project's ./composer.lock
62 4
            getcwd() . '/composer.lock',
63
            // This package's composer.lock
64
            __DIR__ . '/../../composer.lock',
65
        ];
66
67 4
        foreach ($checkedPaths as $path) {
68 4
            if (file_exists($path)) {
69 4
                return $path;
70
            }
71
        }
72
73 1
        throw new UnexpectedValueException(sprintf(
74
            'PackageVersions could not locate the `vendor/composer/installed.json` or your `composer.lock` '
75
            . 'location. This is assumed to be in %s. If you customized your composer vendor directory and ran composer '
76
            . 'installation with --no-scripts or if you deployed without the required composer files, then you are on '
77 1
            . 'your own, and we can\'t really help you. Fix your shit and cut the tooling some slack.',
78 1
            json_encode($checkedPaths)
79
        ));
80
    }
81
82 3
    private static function getVersions(string $composerLockFile) : Generator
83
    {
84 3
        $lockData = json_decode(file_get_contents($composerLockFile), true);
85
86 3
        if (array_key_exists('content-hash', $lockData)) {
87
            // assume a composer.lock file and merge the packages and packages-dev into an array
88
            $lockData = array_merge($lockData['packages'], $lockData['packages-dev'] ?? []);
89
        }
90
91 3
        foreach ($lockData as $package) {
92 3
            yield $package['name'] => $package['version'] . '@' . (
93 3
                $package['source']['reference'] ?? $package['dist']['reference'] ?? ''
94
            );
95
        }
96
97 3
        yield self::ROOT_PACKAGE_NAME;
98 3
    }
99
}
100