Completed
Push — master ( 55ae92...17bdf6 )
by Nikola
08:29
created

ComparisonConstraintParser   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 114
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 97.92%

Importance

Changes 0
Metric Value
wmc 18
lcom 1
cbo 3
dl 0
loc 114
ccs 47
cts 48
cp 0.9792
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A parse() 0 18 3
A isMultiPartConstraint() 0 4 1
A splitConstraintParts() 0 5 1
A buildConstraint() 0 17 4
A parseConstraint() 0 15 3
B buildCompositeConstraint() 0 26 5
A error() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Version\Constraint;
6
7
use Version\Exception\ExceptionInterface;
8
use Version\Exception\InvalidComparisonConstraintStringException;
9
use Version\Version;
10
11
/**
12
 * @author Nikola Posa <[email protected]>
13
 */
14
class ComparisonConstraintParser
15
{
16
    public const OPERATOR_OR = '||';
17
18
    /**
19
     * @var string
20
     */
21
    protected $constraintString;
22
23
    /**
24
     * @var array
25
     */
26
    protected $constraintParts = [];
27
28
    /**
29
     * @param string $constraintString
30
     * @return ComparisonConstraint|CompositeConstraint
31
     */
32 11
    public function parse(string $constraintString)
33
    {
34 11
        $constraintString = trim($constraintString);
35
36 11
        if ('' === $constraintString) {
37 1
            throw InvalidComparisonConstraintStringException::forEmptyString();
38
        }
39
40 10
        $this->constraintString = $constraintString;
41
42 10
        if (! $this->isMultiPartConstraint()) {
43 7
            return $this->buildConstraint($this->constraintString);
44
        }
45
46 3
        $this->splitConstraintParts();
47
48 3
        return $this->buildCompositeConstraint();
49
    }
50
51 10
    protected function isMultiPartConstraint() : bool
52
    {
53 10
        return (false !== strpos($this->constraintString, ' '));
54
    }
55
56 3
    protected function splitConstraintParts() : void
57
    {
58 3
        $constraintParts = explode(' ', $this->constraintString);
59 3
        $this->constraintParts = array_map('trim', $constraintParts);
60 3
    }
61
62 10
    protected function buildConstraint(string $constraintPart) : ComparisonConstraint
63
    {
64 10
        [$operator, $operandString] = array_values($this->parseConstraint($constraintPart));
0 ignored issues
show
Bug introduced by
The variable $operator does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $operandString does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
65
66 10
        if (empty($operandString)) {
67 1
            $this->error();
68
        }
69
70
        try {
71 9
            return new ComparisonConstraint(
72 9
                $operator ?: ComparisonConstraint::OPERATOR_EQ,
73 9
                Version::fromString($operandString)
74
            );
75 2
        } catch (ExceptionInterface $ex) {
76 2
            $this->error();
77
        }
78
    }
79
80 10
    protected function parseConstraint(string $constraintStringUnit) : array
81
    {
82 10
        $i = 0;
83 10
        while (isset($constraintStringUnit[$i]) && !ctype_digit($constraintStringUnit[$i])) {
84 9
            $i++;
85
        }
86
87 10
        $operator = substr($constraintStringUnit, 0, $i);
88 10
        $operand = substr($constraintStringUnit, $i);
89
90
        return [
91 10
            'operator' => $operator,
92 10
            'operand' => $operand,
93
        ];
94
    }
95
96 3
    protected function buildCompositeConstraint() : CompositeConstraint
97
    {
98 3
        $compositeAndConstraints = $compositeOrConstraints = [];
99
100 3
        foreach ($this->constraintParts as $constraintPart) {
101 3
            if (self::OPERATOR_OR === $constraintPart) {
102 2
                $compositeOrConstraints[] = CompositeConstraint::and(...$compositeAndConstraints);
103 2
                $compositeAndConstraints = []; //reset collected AND constraints
104
            } else {
105 3
                $compositeAndConstraints[] = $this->buildConstraint($constraintPart);
106
            }
107
        }
108
109 3
        if (!empty($compositeOrConstraints)) {
110 2
            if (empty($compositeAndConstraints)) {
111
                //invalid OR constraint; no right side
112 1
                $this->error();
113
            }
114
115 1
            $compositeOrConstraints[] = CompositeConstraint::and(...$compositeAndConstraints);
116
117 1
            return CompositeConstraint::or(...$compositeOrConstraints);
118
        }
119
120 1
        return CompositeConstraint::and(...$compositeAndConstraints);
121
    }
122
123 4
    protected function error() : void
124
    {
125 4
        throw InvalidComparisonConstraintStringException::forUnparsableString($this->constraintString);
126
    }
127
}
128