Completed
Pull Request — master (#16)
by Nikola
01:32
created

ComparisonConstraintParser   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 98.18%

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 3
dl 0
loc 123
ccs 54
cts 55
cp 0.9818
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A parse() 0 18 3
A buildConstraintFromStringUnit() 0 17 4
A parseConstraintStringUnit() 0 15 3
A isMultiPartConstraint() 0 4 1
A splitConstraintParts() 0 4 1
B buildCompositeConstraint() 0 33 6
A isOperator() 0 6 1
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\InvalidConstraintStringException;
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 11
    public function parse(string $constraintString) : ConstraintInterface
29
    {
30 11
        $constraintString = trim($constraintString);
31
32 11
        if ('' === $constraintString) {
33 1
            throw InvalidConstraintStringException::forEmptyConstraintString();
34
        }
35
36 10
        $this->constraintString = $constraintString;
37
38 10
        if (! $this->isMultiPartConstraint()) {
39 7
            return $this->buildConstraintFromStringUnit($this->constraintString);
40
        }
41
42 3
        $this->splitConstraintParts();
43
44 3
        return $this->buildCompositeConstraint();
45
    }
46
47 10
    protected function buildConstraintFromStringUnit(string $constraintStringUnit) : ComparisonConstraint
48
    {
49 10
        [$operator, $operandString] = array_values($this->parseConstraintStringUnit($constraintStringUnit));
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...
50
51 10
        if (empty($operandString)) {
52 1
            $this->error();
53
        }
54
55
        try {
56 9
            return new ComparisonConstraint(
57 9
                $operator ?: ComparisonConstraint::OPERATOR_EQ,
58 9
                Version::fromString($operandString)
59
            );
60 2
        } catch (ExceptionInterface $ex) {
61 2
            $this->error();
62
        }
63
    }
64
65 10
    protected function parseConstraintStringUnit(string $constraintStringUnit) : array
66
    {
67 10
        $i = 0;
68 10
        while (isset($constraintStringUnit[$i]) && !ctype_digit($constraintStringUnit[$i])) {
69 9
            $i++;
70
        }
71
72 10
        $operator = substr($constraintStringUnit, 0, $i);
73 10
        $operand = substr($constraintStringUnit, $i);
74
75
        return [
76 10
            'operator' => $operator,
77 10
            'operand' => $operand,
78
        ];
79
    }
80
81 10
    protected function isMultiPartConstraint() : bool
82
    {
83 10
        return (false !== strpos($this->constraintString, ' '));
84
    }
85
86 3
    protected function splitConstraintParts() : void
87
    {
88 3
        $this->constraintParts = explode(' ', $this->constraintString);
89 3
    }
90
91 3
    protected function buildCompositeConstraint() : CompositeConstraint
92
    {
93 3
        $compositeAndConstraints = $compositeOrConstraints = [];
94
95 3
        foreach ($this->constraintParts as $constraintPart) {
96 3
            if (!$this->isOperator($constraintPart)) {
97 3
                $compositeAndConstraints[] = $this->buildConstraintFromStringUnit($constraintPart);
98 3
                continue;
99
            }
100
101 2
            $constraintOperator = $constraintPart;
102
103
            switch ($constraintOperator) {
104 2
                case self::OPERATOR_OR:
105 2
                    $compositeOrConstraints[] = CompositeConstraint::and(...$compositeAndConstraints);
106 2
                    $compositeAndConstraints = [];
107 2
                    break;
108
            }
109
        }
110
111 3
        if (!empty($compositeOrConstraints)) {
112 2
            if (empty($compositeAndConstraints)) {
113
                //invalid OR constraint; no right side
114 1
                $this->error();
115
            }
116
117 1
            $compositeOrConstraints[] = CompositeConstraint::and(...$compositeAndConstraints);
118
119 1
            return CompositeConstraint::or(...$compositeOrConstraints);
120
        }
121
122 1
        return CompositeConstraint::and(...$compositeAndConstraints);
123
    }
124
125 3
    protected function isOperator(string $constraintPart) : bool
126
    {
127 3
        return in_array($constraintPart, [
128 3
            self::OPERATOR_OR,
129 3
        ], true);
130
    }
131
132 4
    protected function error() : void
133
    {
134 4
        throw InvalidConstraintStringException::forConstraintString($this->constraintString);
135
    }
136
}
137