Passed
Push — master ( c80459...f6593c )
by Mihail
02:07
created

semver.php (1 issue)

Labels
Severity
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 of the Koded version.
57
 * Checks the correctness of the provided version array.
58
 *
59
 * @param array $version
60
 * @return array Koded segmented version as array
61
 */
62
function get_complete_version(array $version): array
63
{
64 16
    if (false === empty($version)) {
65 14
        assert(3 === \count($version), 'version array should have exactly 3 parts');
66 14
        assert('' !== $version[1], 'pre-release is empty, should be zero or valid identifier');
67 14
        assert('' !== $version[2], 'build-metadata is empty, should be zero or valid identifier');
68 14
        return $version;
69
    }
70 2
    if (defined('VERSION') && is_array(VERSION)) {
0 ignored issues
show
The constant VERSION was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
71
        return get_version_array(join('-', array_filter(VERSION)));
72
    }
73 2
    if ($version = @file_get_contents(getcwd() . '/VERSION')) {
74 2
        return get_version_array($version);
75
    }
76
    return INVALID_VERSION_ARRAY;
77
}
78
79
/**
80
 * @internal
81
 *
82
 * Returns the the major version from VERSION file.
83
 *
84
 * @param array $version
85
 * @return int The major version
86
 */
87
function get_major_version(array $version): int
88
{
89 1
    return (int)get_complete_version($version)[0];
90
}
91
92
/**
93
 * @internal
94
 *
95
 * The result is the UTC timestamp of the changeset in "YmDHis" format.
96
 * This value is not guaranteed to be unique, but it is sufficient
97
 * for generating the development version numbers.
98
 *
99
 * @return string Returns the numeric identifier of the latest GIT changeset,
100
 * or root directory modification time on failure
101
 */
102
function get_git_changeset(): string
103
{
104 1
    $format = 'YmdHis';
105 1
    $gitlog = proc_open('git log --pretty=format:%ct --quiet -l HEAD', [
106 1
        ['pipe', 'r'],
107
        ['pipe', 'w'],
108
        ['pipe', 'w'],
109 1
    ], $pipes, __DIR__);
110 1
    if (false === is_resource($gitlog)) {
111
        return date($format, filemtime(__DIR__ . '/.'));
112
    }
113 1
    stream_set_blocking($pipes[2], false);
114 1
    $timestamp = stream_get_contents($pipes[1]);
115 1
    $timestamp = explode(PHP_EOL, $timestamp)[0] ?? '';
116
    // cleanup; avoid a deadlock
117 1
    fclose($pipes[0]);
118 1
    fclose($pipes[1]);
119 1
    fclose($pipes[2]);
120 1
    proc_close($gitlog);
121 1
    if (empty($timestamp)) {
122 1
        return date($format, filemtime(__DIR__ . '/.'));
123
    }
124
    // UNIX timestamps are stored in UTC
125
    return date_create_immutable('@' . $timestamp)->format($format);
126
}
127