Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
58.62% covered (warning)
58.62%
34 / 58
44.44% covered (danger)
44.44%
4 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
CliVersion
58.62% covered (warning)
58.62%
34 / 58
44.44% covered (danger)
44.44%
4 / 9
93.77
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 tryParseCore
92.31% covered (success)
92.31%
24 / 26
0.00% covered (danger)
0.00%
0 / 1
11.06
 tryParse
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 parse
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 compare
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
90
 toCompare
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isEquals
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __toString
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace PeServer\Core\Version;
6
7use Exception;
8use Stringable;
9use PeServer\Core\Collection\Arr;
10use PeServer\Core\Regex;
11use PeServer\Core\Text;
12use PeServer\Core\Throws\ArgumentException;
13use PeServer\Core\Throws\ParseException;
14use PeServer\Core\Throws\RegexException;
15use PeServer\Core\TypeUtility;
16
17/**
18 * .NET のバージョンクラスと同じ扱い。
19 */
20
21readonly class CliVersion implements Stringable
22{
23    #region define
24
25    public const IGNORE_REVISION = -1;
26
27    #endregion
28
29    /**
30     * 生成
31     *
32     * @param int $major
33     * @phpstan-param non-negative-int $major
34     * @param int $minor
35     * @phpstan-param non-negative-int $minor
36     * @param int $build
37     * @phpstan-param non-negative-int $build
38     * @param int $revision
39     * @phpstan-param self::IGNORE_REVISION|non-negative-int $revision
40     */
41    public function __construct(int $major, int $minor = 0, int $build = 0, int $revision = self::IGNORE_REVISION)
42    {
43        $this->major = $major;
44        $this->minor = $minor;
45        $this->build = $build;
46        $this->revision = 0 <= $revision ? $revision : self::IGNORE_REVISION;
47    }
48
49    #region property
50
51    /**
52     * [1] メジャー バージョン。
53     * @phpstan-var non-negative-int
54     */
55    public int $major;
56    /**
57     * [2] マイナー バージョン。
58     * @phpstan-var non-negative-int
59     */
60    public int $minor;
61    /**
62     * [3] ビルド バージョン。
63     * @phpstan-var non-negative-int
64     */
65    public int $build;
66    /**
67     * [4] リビジョン バージョン。
68     * @phpstan-var -1|non-negative-int
69     */
70    public int $revision;
71
72    #endregion
73
74    #region function
75
76    /**
77     *
78     * @param null|string $s
79     * @param null|CliVersion $result
80     * @return bool
81     * @phpstan-assert-if-true CliVersion $result
82     * @phpstan-assert-if-false null $result
83     * @throws ArgumentException
84     * @throws RegexException
85     */
86    private static function tryParseCore(?string $s, ?CliVersion &$result): bool
87    {
88        if (Text::isNullOrWhiteSpace($s)) {
89            return false;
90        }
91
92        $regex = new Regex();
93        try {
94            $matches = $regex->matches($s, '/^(?<MAJOR>\d+)(\.(?<MINOR>\d+)(\.(?<BUILD>\d+)(\.(?<REVISION>\d+))?)?)?$/');
95            $elementCount = Arr::getCount($matches);
96
97            if ($elementCount === 0) {
98                return false;
99            }
100
101            $major = 0;
102            $minor = 0;
103            $build = 0;
104            $revision = self::IGNORE_REVISION;
105
106            if (isset($matches['MAJOR'])) {
107                $major = TypeUtility::parseUInteger($matches['MAJOR']);
108            }
109            if (isset($matches['MINOR'])) {
110                if (TypeUtility::tryParseUInteger($matches['MINOR'], $value)) {
111                    $minor = $value;
112                }
113            }
114            if (isset($matches['BUILD'])) {
115                if (TypeUtility::tryParseUInteger($matches['BUILD'], $value)) {
116                    $build = $value;
117                }
118            }
119            if (isset($matches['REVISION'])) {
120                if (TypeUtility::tryParseUInteger($matches['REVISION'], $value)) {
121                    $revision = $value;
122                }
123            }
124
125            $result = new self($major, $minor, $build, $revision);
126
127            return true;
128        } catch (Exception) {
129            return false;
130        }
131    }
132
133    public static function tryParse(?string $s, ?CliVersion &$result): bool
134    {
135        return self::tryParseCore($s, $result);
136    }
137
138    public static function parse(?string $s): CliVersion
139    {
140        if (self::tryParseCore($s, $result)) {
141            return $result;
142        }
143
144        throw new ParseException();
145    }
146
147    public static function compare(CliVersion $a, CliVersion $b): int
148    {
149        if ($a->major != $b->major) {
150            if ($a->major > $b->major) {
151                return 1;
152            }
153            return -1;
154        }
155
156        if ($a->minor != $b->minor) {
157            if ($a->minor > $b->minor) {
158                return 1;
159            }
160            return -1;
161        }
162
163        if ($a->build != $b->build) {
164            if ($a->build > $b->build) {
165                return 1;
166            }
167            return -1;
168        }
169
170        if ($a->revision != $b->revision) {
171            if ($a->revision > $b->revision) {
172                return 1;
173            }
174            return -1;
175        }
176
177        return 0;
178    }
179
180    public function toCompare(CliVersion $version): int
181    {
182        return self::compare($this, $version);
183    }
184
185    public function isEquals(CliVersion $version): bool
186    {
187        return !self::compare($this, $version);
188    }
189
190    public function toString(): string
191    {
192        return $this->__toString();
193    }
194
195    #endregion
196
197    #region Stringable
198
199    public function __toString(): string
200    {
201        $result = "{$this->major}.{$this->minor}.{$this->build}";
202        if ($this->revision !== self::IGNORE_REVISION) {
203            $result .= ".{$this->revision}";
204        }
205
206        return $result;
207    }
208
209    #endregion
210}