Completed
Push — master ( f9d64c...c8bd50 )
by brian
02:06
created

HyphenatedRangeParser::getSingleVersionTokens()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 35
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 7.004

Importance

Changes 0
Metric Value
dl 0
loc 35
ccs 22
cts 23
cp 0.9565
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 23
nc 5
nop 1
crap 7.004
1
<?php
2
3
/**
4
 * @copyright   (c) 2014-2017 brian ridley
5
 * @author      brian ridley <[email protected]>
6
 * @license     http://opensource.org/licenses/MIT MIT
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ptlis\SemanticVersion\Parse\RangeMatcher;
13
14
use ptlis\SemanticVersion\Comparator\ComparatorInterface;
15
use ptlis\SemanticVersion\Parse\Token;
16
use ptlis\SemanticVersion\Parse\VersionParser;
17
use ptlis\SemanticVersion\Version\Version;
18
use ptlis\SemanticVersion\VersionRange\ComparatorVersion;
19
use ptlis\SemanticVersion\VersionRange\LogicalAnd;
20
use ptlis\SemanticVersion\VersionRange\VersionRangeInterface;
21
22
/**
23
 * Parser for hyphenated ranges.
24
 *
25
 * Hyphenated ranges are implemented as described @ https://getcomposer.org/doc/articles/versions.md#range-hyphen-
26
 */
27
final class HyphenatedRangeParser implements RangeParserInterface
28
{
29
    use ChunkByDash;
30
31
    /** @var VersionParser */
32
    private $versionParser;
33
34
    /** @var ComparatorInterface */
35
    private $greaterOrEqualTo;
36
37
    /** @var ComparatorInterface */
38
    private $lessThan;
39
40
    /** @var ComparatorInterface */
41
    private $lessOrEqualTo;
42
43
    /** @var string[][] */
44
    private $validConfigurations = [
45
        [Token::DIGITS, Token::DIGITS],
46
        [Token::DIGITS, Token::LABEL_STRING, Token::DIGITS],
47
        [Token::DIGITS, Token::DIGITS, Token::LABEL_STRING],
48
        [Token::DIGITS, Token::LABEL_STRING, Token::DIGITS, Token::LABEL_STRING]
49
    ];
50
51
52
    /**
53
     * Constructor.
54
     *
55
     * @param VersionParser $versionParser
56
     * @param ComparatorInterface $greaterOrEqualTo
57
     * @param ComparatorInterface $lessThan
58
     * @param ComparatorInterface $lessOrEqualTo
59
     */
60 11
    public function __construct(
61
        VersionParser $versionParser,
62
        ComparatorInterface $greaterOrEqualTo,
63
        ComparatorInterface $lessThan,
64
        ComparatorInterface $lessOrEqualTo
65
    ) {
66 11
        $this->versionParser = $versionParser;
67 11
        $this->greaterOrEqualTo = $greaterOrEqualTo;
68 11
        $this->lessThan = $lessThan;
69 11
        $this->lessOrEqualTo = $lessOrEqualTo;
70 11
    }
71
72
    /**
73
     * Returns true if the token list can be parsed as a hyphenated range.
74
     *
75
     * @param Token[] $tokenList
76
     *
77
     * @return boolean
78
     */
79 11
    public function canParse(array $tokenList)
80
    {
81 11
        $isRange = false;
82 11
        $chunkedList = $this->chunk($tokenList);
83 11
        foreach ($this->validConfigurations as $configuration) {
84 11
            list($lowerVersionTokenList, $upperVersionTokenList) = $this->getSingleVersionTokens($chunkedList);
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $lowerVersionTokenList exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
Comprehensibility Naming introduced by
The variable name $upperVersionTokenList exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
85 11
            $isRange = $isRange || (
86 11
                $this->chunksMatchConfiguration($chunkedList, $configuration)
87 11
                && $this->versionParser->parse($lowerVersionTokenList)
88 11
                && $this->versionParser->parse($upperVersionTokenList)
89 11
            );
90 11
        }
91
92 11
        return $isRange;
93
    }
94
95
    /**
96
     * Build a ComparatorVersion representing the hyphenated range.
97
     *
98
     * @param Token[] $tokenList
99
     *
100
     * @return VersionRangeInterface
101
     */
102 10
    public function parse(array $tokenList)
103
    {
104 10
        if (!$this->canParse($tokenList)) {
105 4
            throw new \RuntimeException('Invalid version');
106
        }
107
108 6
        $chunkedList = $this->chunk($tokenList);
109
110 6
        list($lowerVersionTokenList, $upperVersionTokenList) = $this->getSingleVersionTokens($chunkedList);
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $lowerVersionTokenList exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
Comprehensibility Naming introduced by
The variable name $upperVersionTokenList exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
111
112 6
        return new LogicalAnd(
113 6
            new ComparatorVersion(
114 6
                $this->greaterOrEqualTo,
115 6
                $this->versionParser->parse($lowerVersionTokenList)
116 6
            ),
117 6
            $this->getUpperConstraint($upperVersionTokenList)
118 6
        );
119
    }
120
121
    /**
122
     * Returns an array of token arrays, the first is tokens for the lower bound and the second is the upper bound.
123
     *
124
     * @param Token[][] $chunkedList
125
     *
126
     * @return Token[][]
0 ignored issues
show
Documentation introduced by
Should the return type not be array[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
127
     */
128 11
    private function getSingleVersionTokens(
129
        array $chunkedList
130
    ) {
131 11
        $dashTokenList = [new Token(Token::DASH_SEPARATOR, '-')];
132 11
        $lowerTokenList = [];
133 11
        $upperTokenList = [];
134
135 11
        switch (true) {
136
            // No labels
137 11
            case 2 === count($chunkedList):
138 4
                $lowerTokenList = $chunkedList[0];
139 4
                $upperTokenList = $chunkedList[1];
140 4
                break;
141
142
            // Label on first version
143 7
            case 3 === count($chunkedList) && Token::LABEL_STRING === $chunkedList[1][0]->getType():
144 1
                $lowerTokenList = array_merge($chunkedList[0], $dashTokenList, $chunkedList[1]);
145 1
                $upperTokenList = $chunkedList[2];
146 1
                break;
147
148
            // Label on second version
149 6
            case 3 === count($chunkedList) && Token::LABEL_STRING === $chunkedList[2][0]->getType():
150 1
                $lowerTokenList = $chunkedList[0];
151 1
                $upperTokenList = array_merge($chunkedList[1], $dashTokenList, $chunkedList[2]);
152 1
                break;
153
154
            // Label on both versions
155 5
            case 4 === count($chunkedList):
156 2
                $lowerTokenList = array_merge($chunkedList[0], $dashTokenList, $chunkedList[1]);
157 2
                $upperTokenList = array_merge($chunkedList[2], $dashTokenList, $chunkedList[3]);
158 2
                break;
159
        }
160
161 11
        return [$lowerTokenList, $upperTokenList];
162
    }
163
164
    /**
165
     * Returns true if the chunks match the configuration.
166
     *
167
     * @param Token[][] $chunkedList
168
     * @param string[] $configuration
169
     *
170
     * @return boolean
171
     */
172 11
    private function chunksMatchConfiguration(
173
        array $chunkedList,
174
        array $configuration
175
    ) {
176 11
        $matches = count($chunkedList) === count($configuration);
177
178 11
        foreach ($configuration as $index => $token) {
179 11
            if ($matches) {
180 9
                $matches = $chunkedList[$index][0]->getType() === $token;
181 9
            }
182 11
        }
183
184 11
        return $matches;
185
    }
186
187
    /**
188
     * Get the upper version constraint from a token list.
189
     *
190
     * @param Token[] $tokenList
191
     *
192
     * @return ComparatorVersion
193
     */
194 6
    private function getUpperConstraint(array $tokenList)
195
    {
196 6
        $comparator = $this->lessThan;
197 6
        $version = $this->versionParser->parse($tokenList);
198
199 6
        switch (true) {
200 6
            case 1 === count($tokenList):
201 1
                $version = new Version($version->getMajor() + 1, 0, 0);
202 1
                break;
203
204 5
            case 3 === count($tokenList):
205 1
                $version = new Version($version->getMajor(), $version->getMinor() + 1, 0);
206 1
                break;
207
208 4
            case count($tokenList) >= 5:
209 4
                $comparator = $this->lessOrEqualTo;
210 4
                break;
211
        }
212
213 6
        return new ComparatorVersion($comparator, $version);
214
    }
215
}
216