Completed
Push — master ( b0e701...f9d64c )
by brian
02:12
created

HyphenatedRangeParser::getSingleVersionTokens()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 38
ccs 23
cts 23
cp 1
rs 8.439
c 0
b 0
f 0
cc 5
eloc 23
nc 5
nop 1
crap 5
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 Token[][] */
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)
0 ignored issues
show
Documentation introduced by
$configuration is of type array<integer,object<ptl...icVersion\Parse\Token>>, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

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