Completed
Push — master ( 8a9411...e8a2ea )
by Nikola
01:33
created

OperationConstraintParser   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 110
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 110
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
A buildCompositeConstraint() 0 26 5
A error() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Version\Comparison\Constraint;
6
7
use Version\Exception\VersionException;
8
use Version\Comparison\Exception\InvalidConstraintString;
9
use Version\Version;
10
11
class OperationConstraintParser
12
{
13
    public const OPERATOR_OR = '||';
14
15
    /** @var string */
16
    protected $constraintString;
17
18
    /** @var array */
19
    protected $constraintParts = [];
20
21
    /**
22
     * @param string $constraintString
23
     * @return OperationConstraint|CompositeConstraint
24
     */
25 13
    public function parse(string $constraintString)
26
    {
27 13
        $constraintString = trim($constraintString);
28
29 13
        if ('' === $constraintString) {
30 1
            throw InvalidConstraintString::empty();
31
        }
32
33 12
        $this->constraintString = $constraintString;
34
35 12
        if (! $this->isMultiPartConstraint()) {
36 9
            return $this->buildConstraint($this->constraintString);
37
        }
38
39 3
        $this->splitConstraintParts();
40
41 3
        return $this->buildCompositeConstraint();
42
    }
43
44 12
    protected function isMultiPartConstraint(): bool
45
    {
46 12
        return (false !== strpos($this->constraintString, ' '));
47
    }
48
49 3
    protected function splitConstraintParts(): void
50
    {
51 3
        $constraintParts = explode(' ', $this->constraintString);
52 3
        $this->constraintParts = array_map('trim', $constraintParts);
53 3
    }
54
55 12
    protected function buildConstraint(string $constraintPart): OperationConstraint
56
    {
57 12
        [$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...
58
59 12
        if (empty($operandString)) {
60 1
            $this->error();
61
        }
62
63
        try {
64 11
            return new OperationConstraint(
65 11
                $operator ?: OperationConstraint::OPERATOR_EQ,
66 11
                Version::fromString($operandString)
67
            );
68 2
        } catch (VersionException $ex) {
69 2
            $this->error();
70
        }
71
    }
72
73 12
    protected function parseConstraint(string $constraintStringUnit): array
74
    {
75 12
        $i = 0;
76 12
        while (isset($constraintStringUnit[$i]) && !ctype_digit($constraintStringUnit[$i])) {
77 11
            $i++;
78
        }
79
80 12
        $operator = substr($constraintStringUnit, 0, $i);
81 12
        $operand = substr($constraintStringUnit, $i);
82
83
        return [
84 12
            'operator' => $operator,
85 12
            'operand' => $operand,
86
        ];
87
    }
88
89 3
    protected function buildCompositeConstraint(): CompositeConstraint
90
    {
91 3
        $compositeAndConstraints = $compositeOrConstraints = [];
92
93 3
        foreach ($this->constraintParts as $constraintPart) {
94 3
            if (self::OPERATOR_OR === $constraintPart) {
95 2
                $compositeOrConstraints[] = CompositeConstraint::and(...$compositeAndConstraints);
96 2
                $compositeAndConstraints = []; //reset collected AND constraints
97
            } else {
98 3
                $compositeAndConstraints[] = $this->buildConstraint($constraintPart);
99
            }
100
        }
101
102 3
        if (!empty($compositeOrConstraints)) {
103 2
            if (empty($compositeAndConstraints)) {
104
                //invalid OR constraint; no right side
105 1
                $this->error();
106
            }
107
108 1
            $compositeOrConstraints[] = CompositeConstraint::and(...$compositeAndConstraints);
109
110 1
            return CompositeConstraint::or(...$compositeOrConstraints);
111
        }
112
113 1
        return CompositeConstraint::and(...$compositeAndConstraints);
114
    }
115
116 4
    protected function error(): void
117
    {
118 4
        throw InvalidConstraintString::notParsable($this->constraintString);
119
    }
120
}
121