Completed
Push — master ( a498f5...578d3f )
by brian
02:12
created

HyphenatedRangeParser::chunk()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 16
cts 16
cp 1
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 14
nc 6
nop 1
crap 4

1 Method

Rating   Name   Duplication   Size   Complexity  
A HyphenatedRangeParser::getLowerConstraint() 0 7 1
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\Matcher;
13
14
use ptlis\SemanticVersion\Comparator\ComparatorInterface;
15
use ptlis\SemanticVersion\Parse\Token;
16
use ptlis\SemanticVersion\Version\Label\LabelBuilder;
17
use ptlis\SemanticVersion\Version\Version;
18
use ptlis\SemanticVersion\Version\VersionBuilder;
19
use ptlis\SemanticVersion\VersionRange\ComparatorVersion;
20
use ptlis\SemanticVersion\VersionRange\LogicalAnd;
21
use ptlis\SemanticVersion\VersionRange\VersionRangeInterface;
22
23
/**
24
 * Parser for hyphenated ranges.
25
 *
26
 * Hyphenated ranges are implemented as described @ https://getcomposer.org/doc/articles/versions.md#range-hyphen-
27
 */
28
final class HyphenatedRangeParser implements RangeParserInterface
29
{
30
    use ChunkByDash;
31
32
    /** @var VersionBuilder */
33
    private $versionBuilder;
34
35
    /** @var ComparatorInterface */
36
    private $greaterOrEqualTo;
37
38
    /** @var ComparatorInterface */
39
    private $lessThan;
40
41
    /** @var ComparatorInterface */
42
    private $lessOrEqualTo;
43
44
45
    /**
46
     * Constructor.
47
     *
48
     * @param VersionBuilder $versionBuilder
49
     * @param ComparatorInterface $greaterOrEqualTo
50
     * @param ComparatorInterface $lessThan
51
     * @param ComparatorInterface $lessOrEqualTo
52
     */
53 11
    public function __construct(
54
        VersionBuilder $versionBuilder,
55
        ComparatorInterface $greaterOrEqualTo,
56
        ComparatorInterface $lessThan,
57
        ComparatorInterface $lessOrEqualTo
58
    ) {
59 11
        $this->versionBuilder = $versionBuilder;
60 11
        $this->greaterOrEqualTo = $greaterOrEqualTo;
61 11
        $this->lessThan = $lessThan;
62 11
        $this->lessOrEqualTo = $lessOrEqualTo;
63 11
    }
64
65
    /**
66
     * Returns true if the token list can be parsed as a hyphenated range.
67
     *
68
     * @param Token[] $tokenList
69
     *
70
     * @return boolean
71
     */
72 11
    public function canParse(array $tokenList)
73
    {
74
        $validConfigurations = [
75 11
            [Token::DIGITS, Token::DIGITS],
76 11
            [Token::DIGITS, Token::LABEL_STRING, Token::DIGITS],
77 11
            [Token::DIGITS, Token::DIGITS, Token::LABEL_STRING],
78 11
            [Token::DIGITS, Token::LABEL_STRING, Token::DIGITS, Token::LABEL_STRING]
79 11
        ];
80
81 11
        $isRange = false;
82 11
        $chunkedList = $this->chunk($tokenList);
83 11
        foreach ($validConfigurations as $configuration) {
84 11
            $isRange = $isRange || $this->chunksMatchConfiguration($chunkedList, $configuration);
85 11
        }
86
87 11
        return $isRange;
88
    }
89
90
    /**
91
     * Returns true if the provided token
92
     *
93
     * @param Token[][] $chunkedList
94
     * @param string[] $configuration
95
     *
96
     * @return boolean
97
     */
98 11
    private function chunksMatchConfiguration(
99
        array $chunkedList,
100
        array $configuration
101
    ) {
102 11
        $matches = count($chunkedList) === count($configuration);
103
104 11
        foreach ($configuration as $index => $token) {
105 11
            if ($matches) {
106 9
                $matches = $chunkedList[$index][0]->getType() === $token;
107 9
            }
108 11
        }
109
110 11
        return $matches;
111
    }
112
113
    /**
114
     * Build a ComparatorVersion representing the hyphenated range.
115
     *
116
     * @param Token[] $tokenList
117
     *
118
     * @return VersionRangeInterface
119
     */
120 10
    public function parse(array $tokenList)
121
    {
122 10
        if (!$this->canParse($tokenList)) {
123 4
            throw new \RuntimeException('Invalid version');
124
        }
125
126 6
        $chunkedList = $this->chunk($tokenList);
127
128 6
        switch (count($chunkedList)) {
1 ignored issue
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
129
130
            // No labels
131 6
            case 2:
132 3
                $lowerVersionConstraint = $this->getLowerConstraint($chunkedList[0]);
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $lowerVersionConstraint 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 3
                $upperVersionConstraint = $this->getUpperConstraint($chunkedList[1]);
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $upperVersionConstraint 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 3
                break;
135
136
            // Label on one version
137 3
            case 3:
138
                // Label belongs to first version
139 2
                if (Token::LABEL_STRING === $chunkedList[1][0]->getType()) {
140 1
                    $lowerVersionConstraint = $this->getLowerConstraint($chunkedList[0], $chunkedList[1]);
141 1
                    $upperVersionConstraint = $this->getUpperConstraint($chunkedList[2]);
142
143
                // Label belongs to second version
144 1
                } else {
145 1
                    $lowerVersionConstraint = $this->getLowerConstraint($chunkedList[0]);
146 1
                    $upperVersionConstraint = $this->getUpperConstraint($chunkedList[1], $chunkedList[2]);
147
                }
148
149 2
                break;
150
151
            // Label on both versions
152 1
            case 4:
153 1
                $lowerVersionConstraint = $this->getLowerConstraint($chunkedList[0], $chunkedList[1]);
154 1
                $upperVersionConstraint = $this->getUpperConstraint($chunkedList[2], $chunkedList[3]);
155 1
                break;
156 6
        }
157
158 6
        return new LogicalAnd(
159 6
            $lowerVersionConstraint,
160
            $upperVersionConstraint
161 6
        );
162
    }
163
164
    /**
165
     * Determines the correct lower version constraint for a hyphenated range.
166
     *
167
     * @param Token[] $versionTokenList
168
     * @param Token[] $labelTokenList
169
     *
170
     * @return VersionRangeInterface
171
     */
172 6
    private function getLowerConstraint(array $versionTokenList, array $labelTokenList = [])
173
    {
174 6
        return new ComparatorVersion(
175 6
            $this->greaterOrEqualTo,
176 6
            $this->versionBuilder->buildFromTokens($versionTokenList, $labelTokenList)
0 ignored issues
show
Bug introduced by
It seems like $this->versionBuilder->b...nList, $labelTokenList) can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
177 6
        );
178
    }
179
180
    /**
181
     * Determines the correct upper version constraint for a hyphenated range.
182
     *
183
     * @param Token[] $versionTokenList
184
     * @param Token[] $labelTokenList
185
     *
186
     * @return VersionRangeInterface
187
     */
188 6
    private function getUpperConstraint(array $versionTokenList, array $labelTokenList = [])
189
    {
190 6
        $major = 0;
191 6
        $minor = 0;
192 6
        $patch = 0;
193 6
        $label = null;
0 ignored issues
show
Unused Code introduced by
$label is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
194 6
        $comparator = $this->lessThan;
195 6
        $labelBuilder = new LabelBuilder();
196
197 6
        switch (count($versionTokenList)) {
198 6
            case 1:
199 1
                $major = $versionTokenList[0]->getValue() + 1;
200 1
                break;
201
202 5
            case 3:
203 1
                $major = $versionTokenList[0]->getValue();
204 1
                $minor = $versionTokenList[2]->getValue() + 1;
205 1
                break;
206
207 4
            case 5:
208 4
                $comparator = $this->lessOrEqualTo;
209 4
                $major = $versionTokenList[0]->getValue();
210 4
                $minor = $versionTokenList[2]->getValue();
211 4
                $patch = $versionTokenList[4]->getValue();
212 4
                break;
213 6
        }
214
215 6
        return new ComparatorVersion(
216 6
            $comparator,
217 6
            new Version(
218 6
                $major,
219 6
                $minor,
220 6
                $patch,
221 6
                $labelBuilder->buildFromTokens($labelTokenList)
222 6
            )
223 6
        );
224
    }
225
}
226