Issues (21)

semver.php (4 issues)

1
<?php declare(strict_types=1);
2
3
const INVALID_VERSION_ARRAY = ['0.0.0', '0', '0'];
4
const SEMVER_REGEX = '/^([0-9]+\.[0-9]+\.[0-9]+)(?:\-([a-z0-9-]+(?:\.[a-z0-9-]+)*))?(?:\+([a-z0-9-]+(?:\.[a-z0-9-]+)*))?$/i';
5
6
/**
7
 * Returns the Koded version number from VERSION file.
8
 *
9
 * @param array $version
10
 * @return string SemVer-compliant version
11
 * @see http://semver.org/
12
 */
13
function get_version(array $version = []): string
14
{
15 13
    $v = get_complete_version($version);
16
    // Most common format (X.Y.Z)
17 13
    if (empty($v[1]) && empty($v[2])) {
18 2
        return $v[0];
19
    }
20
    // Special case when pre-release is ALPHA and build is empty
21 11
    if ('alpha' === strtolower($v[1]) && empty($v[2])) {
22 1
        $v[2] = get_git_changeset();
23 1
        return sprintf('%s-%s+%s', ...$v);
24
    }
25 10
    if (empty($v[2])) {
26 4
        return sprintf('%s-%s', ...$v);
27
    }
28 6
    if (empty($v[1])) {
29 2
        $v[1] = $v[2];
30 2
        return sprintf('%s+%s', ...$v);
31
    }
32 4
    return sprintf('%s-%s+%s', ...$v);
33
}
34
35
/**
36
 * @internal
37
 *
38
 * Returns the version parts in array.
39
 *
40
 * @param string $version
41
 * @return array If version is not parsed by the semver rules, returns 0-filled array
42
 */
43
function get_version_array(string $version): array
44
{
45 46
    if (!preg_match(SEMVER_REGEX, trim($version), $match)) {
46 5
        return INVALID_VERSION_ARRAY;
47
    }
48 41
    array_shift($match);
49 41
    $match = array_replace(INVALID_VERSION_ARRAY, $match);
50 41
    return array_map(fn($v) => empty($v) ? '0' : $v, $match);
51
}
52
53
/**
54
 * @internal
55
 *
56
 * Returns the array version from the VERSION file.
57
 * Checks the correctness of the provided version array.
58
 *
59
 * @param array $version
60
 * @return array Segmented version as array
61
 */
62
function get_complete_version(array $version): array
63
{
64 16
    if (empty($version)) {
65 2
        $version = match (true) {
66 2
            defined('VERSION') && is_array(VERSION) => get_version_array(join('-', array_filter(VERSION))),
0 ignored issues
show
The constant VERSION was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
67 2
            is_file($version = __DIR__ . '/../../../VERSION'), // project dir relative to /vendor
68 2
            is_file($version = getcwd() . '/VERSION') => get_version_array(@file_get_contents($version)),
0 ignored issues
show
It seems like @file_get_contents($version) can also be of type false; however, parameter $version of get_version_array() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

68
            is_file($version = getcwd() . '/VERSION') => get_version_array(/** @scrutinizer ignore-type */ @file_get_contents($version)),
Loading history...
69
            // @codeCoverageIgnoreStart
70
            default => INVALID_VERSION_ARRAY
71
            // @codeCoverageIgnoreEnd
72 2
        };
73
    }
74 16
    assert(3 === count($version), 'version array should have exactly 3 parts');
0 ignored issues
show
It seems like $version can also be of type string; however, parameter $value of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

74
    assert(3 === count(/** @scrutinizer ignore-type */ $version), 'version array should have exactly 3 parts');
Loading history...
75 16
    assert('' !== $version[1], 'pre-release is empty, should be zero or valid identifier');
76 16
    assert('' !== $version[2], 'build-metadata is empty, should be zero or valid identifier');
77 16
    return $version;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $version could return the type string which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
78
}
79
80
/**
81
 * @internal
82
 *
83
 * Returns the the major version from VERSION file.
84
 *
85
 * @param array $version
86
 * @return int The major version
87
 */
88
function get_major_version(array $version): int
89
{
90 1
    return (int)get_complete_version($version)[0];
91
}
92
93
/**
94
 * @internal
95
 *
96
 * The result is the UTC timestamp of the changeset in "YmDHis" format.
97
 * This value is not guaranteed to be unique, but it is sufficient
98
 * for generating the development version numbers.
99
 *
100
 * @return string Returns the numeric identifier of the latest GIT changeset,
101
 * or root directory modification time on failure
102
 */
103
function get_git_changeset(): string
104
{
105 1
    $cwd = getcwd() . '/.';
106 1
    $format = 'YmdHis';
107 1
    $gitlog = proc_open('git log --pretty=format:%ct --quiet -l HEAD', [
108 1
        ['pipe', 'r'],
109 1
        ['pipe', 'w'],
110 1
        ['pipe', 'w'],
111 1
    ], $pipes, $cwd);
112 1
    if (false === is_resource($gitlog)) {
113
        return date($format, filemtime($cwd));
114
    }
115 1
    stream_set_blocking($pipes[2], false);
116 1
    $timestamp = stream_get_contents($pipes[1]);
117 1
    $timestamp = explode(PHP_EOL, $timestamp)[0] ?? '';
118
    // cleanup; avoid a deadlock
119 1
    fclose($pipes[0]);
120 1
    fclose($pipes[1]);
121 1
    fclose($pipes[2]);
122 1
    proc_close($gitlog);
123 1
    if (empty($timestamp)) {
124 1
        return date($format, filemtime($cwd));
125
    }
126
    // UNIX timestamps are stored in UTC
127
    return date_create_immutable('@' . $timestamp)->format($format);
128
}
129