Completed
Push — master ( 85213f...082d37 )
by brian
02:13
created

VersionRangeParser::parseRange()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 14
cts 14
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 13
nc 4
nop 1
crap 4
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;
13
14
use ptlis\SemanticVersion\Parse\RangeMatcher\RangeParserInterface;
15
use ptlis\SemanticVersion\VersionRange\VersionRangeInterface;
16
17
/**
18
 * Parser accepting array of tokens and returning an array of comparators & versions.
19
 */
20
final class VersionRangeParser
21
{
22
    /** @var LogicalOperatorProcessor */
23
    private $logicalOperatorProcessor;
24
25
    /** @var RangeParserInterface[] */
26
    private $rangeParserList;
27
28
    /** @var string[] Array of tokens representing logical operators */
29
    private $operatorTokenList = [
30
        Token::LOGICAL_AND,
31
        Token::LOGICAL_OR
32
    ];
33
34
35
    /**
36
     * Constructor
37
     *
38
     * @param LogicalOperatorProcessor $logicalOperatorProcessor
39
     */
40 32
    public function __construct(
41
        LogicalOperatorProcessor $logicalOperatorProcessor,
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $logicalOperatorProcessor 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...
42
        array $rangeParserList
43
    ) {
44 32
        $this->logicalOperatorProcessor = $logicalOperatorProcessor;
45 32
        $this->rangeParserList = $rangeParserList;
46 32
    }
47
48
    /**
49
     * Parse a version range.
50
     *
51
     * @param Token[] $tokenList
52
     *
53
     * @return VersionRangeInterface
54
     */
55 32
    public function parseRange(array $tokenList)
56
    {
57 32
        $realResultList = [];
58 32
        $tokenClusterList = $this->clusterTokens($tokenList);
59 32
        foreach ($tokenClusterList as $tokenCluster) {
60 32
            $parsed = $this->attemptParse($tokenCluster);
61
62 32
            if (is_null($parsed)) {
63 7
                if (in_array($tokenCluster[0]->getType(), $this->operatorTokenList)) {
64 6
                    $realResultList[] = $tokenCluster[0];
65 6
                } else {
66 1
                    throw new \RuntimeException('Unable to parse version string');
67
                }
68 6
            } else {
69 31
                $realResultList[] = $parsed;
70
            }
71 31
        }
72
73 31
        return $this->logicalOperatorProcessor->run($realResultList);
74
    }
75
76
    /**
77
     * Attempt to parse the token list as a version range into an object implementing VersionRangeInterface
78
     *
79
     * Iterates through the provided range parsers checking to see if they can parse the token list. If they can then we
80
     * call the parse method and return a version range object, otherwise return null.
81
     *
82
     * @param Token[] $tokenList
83
     *
84
     * @return VersionRangeInterface|null
85
     */
86 32
    private function attemptParse(array $tokenList)
87
    {
88 32
        $parsed = null;
89 32
        foreach ($this->rangeParserList as $rangeParser) {
90 32
            if ($rangeParser->canParse($tokenList)) {
91 31
                $parsed = $rangeParser->parse($tokenList);
92 31
                break;
93
            }
94 32
        }
95
96 32
        return $parsed;
97
    }
98
99
    /**
100
     * Clusters the tokens, breaking them up upon finding a logical AND / OR.
101
     *
102
     * @param Token[] $tokenList
103
     *
104
     * @return Token[][] $tokenList
105
     */
106 32
    private function clusterTokens(array $tokenList)
107
    {
108
        $comparatorTokenList = [
109 32
            Token::LOGICAL_AND,
110
            Token::LOGICAL_OR
111 32
        ];
112
113 32
        $tokenClusterList = [];
114
115
        // Stores tokens not yet parcelled out
116 32
        $tokenAccumulator = [];
117 32
        $tokenListCount = count($tokenList);
118 32
        for ($i = 0; $i < $tokenListCount; $i++) {
119 32
            $currentToken = $tokenList[$i];
120
121 32
            if (in_array($currentToken->getType(), $comparatorTokenList)) {
122 6
                $tokenClusterList[] = $tokenAccumulator;
123 6
                $tokenClusterList[] = [$currentToken];
124 6
                $tokenAccumulator = [];
125
126 6
            } else {
127 32
                $tokenAccumulator[] = $currentToken;
128
            }
129 32
        }
130
131
        // Add any remaining tokens
132 32
        if (count($tokenAccumulator)) {
133 32
            $tokenClusterList[] = $tokenAccumulator;
134 32
        }
135
136 32
        return $tokenClusterList;
137
    }
138
}
139