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

src/Parse/RangeMatcher/HyphenatedRangeParser.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\Label\LabelBuilder;
18
use ptlis\SemanticVersion\Version\Version;
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 VersionParser */
33
    private $versionParser;
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 VersionParser $versionParser
49
     * @param ComparatorInterface $greaterOrEqualTo
50
     * @param ComparatorInterface $lessThan
51
     * @param ComparatorInterface $lessOrEqualTo
52
     */
53 11
    public function __construct(
54
        VersionParser $versionParser,
55
        ComparatorInterface $greaterOrEqualTo,
56
        ComparatorInterface $lessThan,
57
        ComparatorInterface $lessOrEqualTo
58
    ) {
59 11
        $this->versionParser = $versionParser;
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
            // TODO: Needs to check for valid verisons too (by joining to label etc)
86 11
        }
87
88 11
        return $isRange;
89
    }
90
91
    /**
92
     * Returns true if the provided token
93
     *
94
     * @param Token[][] $chunkedList
95
     * @param string[] $configuration
96
     *
97
     * @return boolean
98
     */
99 11
    private function chunksMatchConfiguration(
100
        array $chunkedList,
101
        array $configuration
102
    ) {
103 11
        $matches = count($chunkedList) === count($configuration);
104
105 11
        foreach ($configuration as $index => $token) {
106 11
            if ($matches) {
107 9
                $matches = $chunkedList[$index][0]->getType() === $token;
108 9
            }
109 11
        }
110
111 11
        return $matches;
112
    }
113
114
    /**
115
     * Build a ComparatorVersion representing the hyphenated range.
116
     *
117
     * @param Token[] $tokenList
118
     *
119
     * @return VersionRangeInterface
120
     */
121 10
    public function parse(array $tokenList)
122
    {
123 10
        if (!$this->canParse($tokenList)) {
124 4
            throw new \RuntimeException('Invalid version');
125
        }
126
127 6
        $chunkedList = $this->chunk($tokenList);
128
129 6
        switch (count($chunkedList)) {
130
131
            // No labels
132 6
            case 2:
133 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...
134 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...
135 3
                break;
136
137
            // Label on one version
138 3
            case 3:
139
                // Label belongs to first version
140 2
                if (Token::LABEL_STRING === $chunkedList[1][0]->getType()) {
141 1
                    $lowerVersionConstraint = $this->getLowerConstraint($chunkedList[0], $chunkedList[1]);
142 1
                    $upperVersionConstraint = $this->getUpperConstraint($chunkedList[2]);
143
144
                // Label belongs to second version
145 1
                } else {
146 1
                    $lowerVersionConstraint = $this->getLowerConstraint($chunkedList[0]);
147 1
                    $upperVersionConstraint = $this->getUpperConstraint($chunkedList[1], $chunkedList[2]);
148
                }
149
150 2
                break;
151
152
            // Label on both versions
153 1
            case 4:
154 1
                $lowerVersionConstraint = $this->getLowerConstraint($chunkedList[0], $chunkedList[1]);
155 1
                $upperVersionConstraint = $this->getUpperConstraint($chunkedList[2], $chunkedList[3]);
156 1
                break;
157 6
        }
158
159 6
        return new LogicalAnd(
160 6
            $lowerVersionConstraint,
161
            $upperVersionConstraint
162 6
        );
163
    }
164
165
    /**
166
     * Determines the correct lower version constraint for a hyphenated range.
167
     *
168
     * @param Token[] $versionTokenList
169
     * @param Token[] $labelTokenList
170
     *
171
     * @return VersionRangeInterface
172
     */
173 6
    private function getLowerConstraint(array $versionTokenList, array $labelTokenList = [])
174
    {
175 6
        $tokenList = $versionTokenList;
176 6
        if (count($labelTokenList)) {
177 2
            $tokenList[] = new Token(Token::DASH_SEPARATOR, '-');
178 2
            $tokenList = array_merge($tokenList, $labelTokenList);
179 2
        }
180
181 6
        return new ComparatorVersion(
182 6
            $this->greaterOrEqualTo,
183 6
            $this->versionParser->parse($tokenList)
184 6
        );
185
    }
186
187
    /**
188
     * Determines the correct upper version constraint for a hyphenated range.
189
     *
190
     * @param Token[] $versionTokenList
191
     * @param Token[] $labelTokenList
192
     *
193
     * @return VersionRangeInterface
194
     */
195 6
    private function getUpperConstraint(array $versionTokenList, array $labelTokenList = [])
196
    {
197 6
        $major = 0;
198 6
        $minor = 0;
199 6
        $patch = 0;
200 6
        $comparator = $this->lessThan;
201 6
        $labelBuilder = new LabelBuilder();
202
203 6
        switch (count($versionTokenList)) {
204 6
            case 1:
205 1
                $major = $versionTokenList[0]->getValue() + 1;
206 1
                break;
207
208 5
            case 3:
209 1
                $major = $versionTokenList[0]->getValue();
210 1
                $minor = $versionTokenList[2]->getValue() + 1;
211 1
                break;
212
213 4
            case 5:
214 4
                $comparator = $this->lessOrEqualTo;
215 4
                $major = $versionTokenList[0]->getValue();
216 4
                $minor = $versionTokenList[2]->getValue();
217 4
                $patch = $versionTokenList[4]->getValue();
218 4
                break;
219 6
        }
220
221 6
        return new ComparatorVersion(
222 6
            $comparator,
223 6
            new Version(
224 6
                $major,
225 6
                $minor,
226 6
                $patch,
227 6
                $labelBuilder->buildFromTokens($labelTokenList)
228 6
            )
229 6
        );
230
    }
231
}
232