Skip to content

Commit c534e03

Browse files
committed
Split off some specialized classes.
Easier to maintain and understand.
1 parent 1effd15 commit c534e03

6 files changed

Lines changed: 511 additions & 0 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Mistralys\VersionParser\Parser;
6+
7+
use Mistralys\VersionParser\VersionTag;
8+
9+
class BuildNumberGenerator
10+
{
11+
private float $buildNumber;
12+
13+
public function __construct(int $major, int $minor, int $patch, ?VersionTag $tag)
14+
{
15+
$parts = array(
16+
$major,
17+
sprintf('%03d', $minor),
18+
sprintf('%03d', $patch)
19+
);
20+
21+
$number = (float)implode('', $parts);
22+
23+
if($tag !== null) {
24+
$number -= (float)$this->formatTagNumber($tag->getWeight(), $tag->getNumber());
25+
}
26+
27+
$this->buildNumber = $number;
28+
}
29+
30+
public function getBuildNumber() : float
31+
{
32+
return $this->buildNumber;
33+
}
34+
35+
private function formatTagNumber(int $weight, int $number) : string
36+
{
37+
if($weight === 0) {
38+
return '';
39+
}
40+
41+
$positions = 2 * 3;
42+
43+
$number = sprintf('%0'.$weight.'d', $number);
44+
$number = str_pad($number, $positions, '0', STR_PAD_RIGHT);
45+
46+
$number = (int)str_repeat('9', $positions) - (int)$number;
47+
return '.'.$number;
48+
}
49+
}

src/Parser/ComponentDetector.php

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
<?php
2+
/**
3+
* @package VersionParser
4+
* @see \Mistralys\VersionParser\Parser\ComponentDetector
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Mistralys\VersionParser\Parser;
10+
11+
use Mistralys\VersionParser\VersionParser;
12+
use Mistralys\VersionParser\VersionTag;
13+
14+
/**
15+
* Used by the version parser to detect the components
16+
* of the tag part of a version string, which may contain
17+
* a tag type (alpha, beta, etc.) and a branch name.
18+
*
19+
* @package VersionParser
20+
* @author Sebastian Mordziol <s.mordziol@mistralys.eu>
21+
*/
22+
class ComponentDetector
23+
{
24+
private string $tagList = '';
25+
private ?VersionTag $tag = null;
26+
private VersionParser $version;
27+
private string $tagParts;
28+
29+
public function __construct(VersionParser $version, string $tagParts)
30+
{
31+
$this->tagParts = $tagParts;
32+
$this->version = $version;
33+
}
34+
35+
public function detectTag() : ?VersionTag
36+
{
37+
$this->tagList = implode('|', array_keys(TagWeights::getWeights()));
38+
39+
if ($this->parseTagSimple()) {
40+
return $this->tag;
41+
}
42+
43+
if ($this->parseBranchAppend()) {
44+
return $this->tag;
45+
}
46+
47+
if ($this->parseBranchPrepend()) {
48+
return $this->tag;
49+
}
50+
51+
if($this->parseTagAnywhere()) {
52+
return $this->tag;
53+
}
54+
55+
$this->parseBranchOnly();
56+
57+
return $this->tag;
58+
}
59+
60+
private function parseBranchOnly() : void
61+
{
62+
preg_match(
63+
'/\A([a-z0-9-]+)\Z/i',
64+
$this->tagParts,
65+
$matches
66+
);
67+
68+
if(empty($matches[0])) {
69+
return;
70+
}
71+
72+
$this->tag = new VersionTag(
73+
$this->version,
74+
'',
75+
0,
76+
$this->detectOriginalBranch($matches[1])
77+
);
78+
}
79+
80+
private function parseBranchPrepend() : bool
81+
{
82+
preg_match(
83+
'/\A([a-z0-9-]+)-('.$this->tagList.')-?([0-9]+)\Z|\A([a-z0-9-]+)-('.$this->tagList.')\Z/i',
84+
$this->tagParts,
85+
$matches
86+
);
87+
88+
if(empty($matches[0])) {
89+
return false;
90+
}
91+
92+
$matches = $this->nullifyEmpty($matches);
93+
$tag = $matches[2] ?? $matches[5];
94+
$number = $matches[3] ?? 0;
95+
$branch = $this->detectOriginalBranch($matches[1] ?? $matches[4]);
96+
97+
$this->tag = new VersionTag(
98+
$this->version,
99+
$tag,
100+
(int)$number,
101+
$branch
102+
);
103+
104+
return true;
105+
}
106+
107+
private function parseTagSimple() : bool
108+
{
109+
return $this->parseTagSolo();
110+
}
111+
112+
private function parseTagAnywhere() : bool
113+
{
114+
return $this->parseTagSolo(true);
115+
}
116+
117+
private function parseTagSolo(bool $anywhere=false) : bool
118+
{
119+
if(!$anywhere)
120+
{
121+
preg_match(
122+
'/\A(' . $this->tagList . ')-?([0-9]+)\Z|\A(' . $this->tagList . ')\Z/i',
123+
$this->tagParts,
124+
$matches
125+
);
126+
}
127+
else
128+
{
129+
preg_match(
130+
'/-('.$this->tagList.')-?([0-9]+)-|-('.$this->tagList.')-/i',
131+
$this->tagParts,
132+
$matches
133+
);
134+
}
135+
136+
if(empty($matches[0])) {
137+
return false;
138+
}
139+
140+
$matches = $this->nullifyEmpty($matches);
141+
$tag = $matches[1] ?? $matches[3];
142+
$number = $matches[2] ?? 0;
143+
144+
$branch = str_replace($matches[0], '-', $this->tagParts);
145+
146+
while(strpos($branch, '--') !== false) {
147+
$branch = str_replace('--', '-', $branch);
148+
}
149+
150+
$branch = trim($branch, '-');
151+
152+
$this->tag = new VersionTag(
153+
$this->version,
154+
$tag,
155+
(int)$number,
156+
$branch
157+
);
158+
159+
return true;
160+
}
161+
162+
private function parseBranchAppend() : bool
163+
{
164+
preg_match(
165+
'/\A('.$this->tagList.')-?([0-9]+)-([a-z0-9-]+)\Z|\A('.$this->tagList.')-([a-z0-9-]+)\Z/i',
166+
$this->tagParts,
167+
$matches
168+
);
169+
170+
if(empty($matches[0])) {
171+
return false;
172+
}
173+
174+
$matches = $this->nullifyEmpty($matches);
175+
$tag = $matches[1] ?? $matches[4];
176+
$number = $matches[2] ?? 0;
177+
$branch = $matches[3] ?? $matches[5];
178+
179+
$this->tag = new VersionTag(
180+
$this->version,
181+
$tag,
182+
(int)$number,
183+
$this->detectOriginalBranch($branch)
184+
);
185+
186+
return true;
187+
}
188+
189+
private function nullifyEmpty(array $values) : array
190+
{
191+
foreach($values as $idx => $value) {
192+
if(empty($value)) {
193+
$values[$idx] = null;
194+
}
195+
}
196+
197+
return $values;
198+
}
199+
200+
/**
201+
* Because of the way the version string is split, the
202+
* information about how the original branch name was
203+
* formatted is lost. This uses the normalized name to
204+
* detect the original branch name.
205+
*
206+
* @param string $branch
207+
* @return string
208+
*/
209+
private function detectOriginalBranch(string $branch) : string
210+
{
211+
if(empty($branch)) {
212+
return '';
213+
}
214+
215+
$branch = str_replace('-', '_SEP_', $branch);
216+
217+
preg_match('/'.str_replace('_SEP_', '.+', preg_quote($branch)).'/', $this->version->getOriginalString(), $matches);
218+
219+
if(!empty($matches[0])) {
220+
return $matches[0];
221+
}
222+
223+
return $branch;
224+
}
225+
}

src/Parser/NumberDetector.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Mistralys\VersionParser\Parser;
6+
7+
class NumberDetector
8+
{
9+
private string $version;
10+
/**
11+
* @var int[]
12+
*/
13+
private array $parts = array();
14+
private string $tagString = '';
15+
16+
public function __construct(string $version)
17+
{
18+
$this->version = $version;
19+
20+
$this->parse();
21+
}
22+
23+
/**
24+
* @return int[]
25+
*/
26+
public function getNumbers() : array
27+
{
28+
return $this->parts;
29+
}
30+
31+
public function getTagString() : string
32+
{
33+
return $this->tagString;
34+
}
35+
36+
private function parse() : void
37+
{
38+
$parts = $this->splitVersionString($this->version);
39+
40+
foreach($parts as $idx => $part)
41+
{
42+
// Continue only as long as we're dealing with numbers
43+
if(is_numeric($part)) {
44+
$this->parts[] = intval($part);
45+
unset($parts[$idx]);
46+
} else {
47+
break;
48+
}
49+
}
50+
51+
while(count($this->parts) < 3) {
52+
$this->parts[] = 0;
53+
}
54+
55+
if(count($this->parts) > 3) {
56+
$this->parts = array_slice($this->parts, 0, 3);
57+
}
58+
59+
$this->tagString = trim(implode('-', $parts), '-');
60+
}
61+
62+
/**
63+
* Splits the version string into an indexed array of parts,
64+
* delimited by dots, dashes, underscores, spaces, tabs and
65+
* newlines.
66+
*
67+
* @param string $version
68+
* @return string[]
69+
*/
70+
public function splitVersionString(string $version) : array
71+
{
72+
$version = str_replace(SeparatorChars::CHARS, '.', $version);
73+
74+
while(strpos($version, '..') !== false) {
75+
$version = str_replace('..', '.', $version);
76+
}
77+
78+
return explode('.', $version);
79+
}
80+
}

src/Parser/SeparatorChars.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Mistralys\VersionParser\Parser;
6+
7+
class SeparatorChars
8+
{
9+
public const CHARS = array(
10+
'.',
11+
'_',
12+
'-',
13+
':',
14+
',',
15+
';',
16+
'!',
17+
'?',
18+
'#',
19+
'`',
20+
'´',
21+
'=',
22+
'~',
23+
'^',
24+
'°',
25+
'+',
26+
'*',
27+
'/',
28+
'(',
29+
')',
30+
'[',
31+
']',
32+
'{',
33+
'}',
34+
'"',
35+
"'",
36+
' ',
37+
"\t",
38+
"\n",
39+
"\r"
40+
);
41+
}

0 commit comments

Comments
 (0)