Completed
Push — master ( 475c0f...a498f5 )
by brian
03:00
created

ComparatorVersionParser   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Coupling/Cohesion

Dependencies 4

Test Coverage

Coverage 96.92%

Importance

Changes 0
Metric Value
wmc 18
cbo 4
dl 0
loc 151
ccs 63
cts 65
cp 0.9692
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
B canParse() 0 18 6
B parse() 0 32 4
A hasIllegalTokens() 0 19 3
B chunk() 0 25 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\Matcher;
13
14
use ptlis\SemanticVersion\Comparator\ComparatorFactory;
15
use ptlis\SemanticVersion\Parse\Token;
16
use ptlis\SemanticVersion\Version\VersionBuilder;
17
use ptlis\SemanticVersion\VersionRange\ComparatorVersion;
18
use ptlis\SemanticVersion\VersionRange\VersionRangeInterface;
19
20
/**
21
 * Comparator versions store a comparator & version specifying part of a version range.
22
 */
23
final class ComparatorVersionParser implements RangeParserInterface
24
{
25
    /** @var ComparatorFactory */
26
    private $comparatorFactory;
27
28
    /** @var VersionBuilder */
29
    private $versionBuilder;
30
31
32
    /**
33
     * Constructor.
34
     *
35
     * @param ComparatorFactory $comparatorFactory
36
     * @param VersionBuilder $versionBuilder
37
     */
38 5
    public function __construct(
39
        ComparatorFactory $comparatorFactory,
40
        VersionBuilder $versionBuilder
41
    ) {
42 5
        $this->comparatorFactory = $comparatorFactory;
43 5
        $this->versionBuilder = $versionBuilder;
44 5
    }
45
46
    /**
47
     * Returns true if the tokens can be parsed as a ComparatorVersion.
48
     *
49
     * @param Token[] $tokenList
50
     *
51
     * @return boolean
52
     */
53 5
    public function canParse(array $tokenList)
54
    {
55 5
        $canParse = false;
56
57
        // No illegal tokens present
58 5
        if (!$this->hasIllegalTokens($tokenList)) {
59 5
            $chunkedList = $this->chunk($tokenList);
60
61
            if (
62 5
                (1 === count($chunkedList) && Token::LABEL_STRING !== $chunkedList[0][0]->getType())
63 1
                || (2 === count($chunkedList) && Token::LABEL_STRING === $chunkedList[1][0]->getType())
64 5
            ) {
65 5
                $canParse = true;
66 5
            }
67 5
        }
68
69 5
        return $canParse;
70
    }
71
72
    /**
73
     * Build a ComparatorVersion representing the comparator & version.
74
     *
75
     * @param Token[] $tokenList
76
     *
77
     * @return VersionRangeInterface
78
     */
79 5
    public function parse(array $tokenList)
80
    {
81
        $comparatorList = [
82 5
            '<',
83 5
            '<=',
84 5
            '>',
85 5
            '>=',
86
            '='
87 5
        ];
88
89
        // Prefixed comparator, hydrate & remove
90 5
        if (count($tokenList) > 0 && in_array($tokenList[0]->getValue(), $comparatorList)) {
91 4
            $comparator = $this->comparatorFactory->get($tokenList[0]->getValue());
92 4
            $tokenList = array_slice($tokenList, 1);
93
94
        // Default to equality
95 4
        } else {
96 1
            $comparator = $this->comparatorFactory->get('=');
97
        }
98
99 5
        $chunkList = $this->chunk($tokenList);
100 5
        $versionTokenList = $chunkList[0];
101 5
        $labelTokenList = [];
102 5
        if (count($chunkList) > 1) {
103 1
            $labelTokenList = $chunkList[1];
104 1
        }
105
106 5
        return new ComparatorVersion(
107 5
            $comparator,
0 ignored issues
show
Bug introduced by
It seems like $comparator 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...
108 5
            $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...
109 5
        );
110
    }
111
112
    /**
113
     * Returns true if an illegal token is found.
114
     *
115
     * @param Token[] $tokenList
116
     *
117
     * @return boolean
118
     */
119 5
    public function hasIllegalTokens(array $tokenList)
120
    {
121
        $illegalTokenList = [
122 5
            Token::CARET_RANGE,
123 5
            Token::TILDE_RANGE,
124 5
            Token::WILDCARD_DIGITS,
125 5
            Token::LOGICAL_AND,
126
            Token::LOGICAL_OR
127 5
        ];
128
129 5
        $hasIllegalToken = false;
130 5
        foreach ($tokenList as $token) {
131 5
            if (in_array($token->getType(), $illegalTokenList)) {
132
                $hasIllegalToken = true;
133
            }
134 5
        }
135
136 5
        return $hasIllegalToken;
137
    }
138
139
    /**
140
     * Chuck the tokens, splitting on hyphen.
141
     *
142
     * @todo Copy & pasted from hyphenated range parser
143
     *
144
     * @param Token[] $tokenList
145
     *
146
     * @return Token[][]
147
     */
148 5
    private function chunk(array $tokenList)
149
    {
150 5
        $tokenListCount = count($tokenList);
151 5
        $chunkedList = [];
152 5
        $accumulator = [];
153
154 5
        for ($i = 0; $i < $tokenListCount; $i++) {
155 5
            $token = $tokenList[$i];
156
157
            // Accumulate until we hit a dash
158 5
            if (Token::DASH_SEPARATOR !== $token->getType()) {
159 5
                $accumulator[] = $token;
160
1 ignored issue
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
161 5
            } else {
162 1
                $chunkedList[] = $accumulator;
163 1
                $accumulator = [];
164
            }
165 5
        }
166
167 5
        if (count($accumulator)) {
168 5
            $chunkedList[] = $accumulator;
169 5
        }
170
171 5
        return $chunkedList;
172
    }
173
}
174