MultiConstraint::optimizeConstraints()   D
last analyzed

Complexity

Conditions 21
Paths 3

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 22
rs 4.1666
c 0
b 0
f 0
cc 21
nc 3
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace HumbugBox451\Composer\Semver\Constraint;
4
5
/** @internal */
6
class MultiConstraint implements ConstraintInterface
7
{
8
    /**
9
    @phpstan-var
10
    */
11
    protected $constraints;
12
    protected $prettyString;
13
    protected $string;
14
    protected $conjunctive;
15
    protected $lowerBound;
16
    protected $upperBound;
17
    public function __construct(array $constraints, $conjunctive = \true)
18
    {
19
        if (\count($constraints) < 2) {
20
            throw new \InvalidArgumentException('Must provide at least two constraints for a MultiConstraint. Use ' . 'the regular Constraint class for one constraint only or MatchAllConstraint for none. You may use ' . 'MultiConstraint::create() which optimizes and handles those cases automatically.');
21
        }
22
        $this->constraints = $constraints;
23
        $this->conjunctive = $conjunctive;
24
    }
25
    public function getConstraints()
26
    {
27
        return $this->constraints;
28
    }
29
    public function isConjunctive()
30
    {
31
        return $this->conjunctive;
32
    }
33
    public function isDisjunctive()
34
    {
35
        return !$this->conjunctive;
36
    }
37
    public function compile($otherOperator)
38
    {
39
        $parts = array();
40
        foreach ($this->constraints as $constraint) {
41
            $code = $constraint->compile($otherOperator);
42
            if ($code === 'true') {
43
                if (!$this->conjunctive) {
44
                    return 'true';
45
                }
46
            } elseif ($code === 'false') {
47
                if ($this->conjunctive) {
48
                    return 'false';
49
                }
50
            } else {
51
                $parts[] = '(' . $code . ')';
52
            }
53
        }
54
        if (!$parts) {
55
            return $this->conjunctive ? 'true' : 'false';
56
        }
57
        return $this->conjunctive ? \implode('&&', $parts) : \implode('||', $parts);
58
    }
59
    public function matches(ConstraintInterface $provider)
60
    {
61
        if (\false === $this->conjunctive) {
62
            foreach ($this->constraints as $constraint) {
63
                if ($provider->matches($constraint)) {
64
                    return \true;
65
                }
66
            }
67
            return \false;
68
        }
69
        if ($provider instanceof MultiConstraint && $provider->isDisjunctive()) {
70
            return $provider->matches($this);
71
        }
72
        foreach ($this->constraints as $constraint) {
73
            if (!$provider->matches($constraint)) {
74
                return \false;
75
            }
76
        }
77
        return \true;
78
    }
79
    public function setPrettyString($prettyString)
80
    {
81
        $this->prettyString = $prettyString;
82
    }
83
    public function getPrettyString()
84
    {
85
        if ($this->prettyString) {
86
            return $this->prettyString;
87
        }
88
        return (string) $this;
89
    }
90
    public function __toString()
91
    {
92
        if ($this->string !== null) {
93
            return $this->string;
94
        }
95
        $constraints = array();
96
        foreach ($this->constraints as $constraint) {
97
            $constraints[] = (string) $constraint;
98
        }
99
        return $this->string = '[' . \implode($this->conjunctive ? ' ' : ' || ', $constraints) . ']';
100
    }
101
    public function getLowerBound()
102
    {
103
        $this->extractBounds();
104
        if (null === $this->lowerBound) {
105
            throw new \LogicException('extractBounds should have populated the lowerBound property');
106
        }
107
        return $this->lowerBound;
108
    }
109
    public function getUpperBound()
110
    {
111
        $this->extractBounds();
112
        if (null === $this->upperBound) {
113
            throw new \LogicException('extractBounds should have populated the upperBound property');
114
        }
115
        return $this->upperBound;
116
    }
117
    public static function create(array $constraints, $conjunctive = \true)
118
    {
119
        if (0 === \count($constraints)) {
120
            return new MatchAllConstraint();
121
        }
122
        if (1 === \count($constraints)) {
123
            return $constraints[0];
124
        }
125
        $optimized = self::optimizeConstraints($constraints, $conjunctive);
126
        if ($optimized !== null) {
127
            list($constraints, $conjunctive) = $optimized;
128
            if (\count($constraints) === 1) {
129
                return $constraints[0];
130
            }
131
        }
132
        return new self($constraints, $conjunctive);
133
    }
134
    /**
135
    @phpstan-return
136
    */
137
    private static function optimizeConstraints(array $constraints, $conjunctive)
138
    {
139
        if (!$conjunctive) {
140
            $left = $constraints[0];
141
            $mergedConstraints = array();
142
            $optimized = \false;
143
            for ($i = 1, $l = \count($constraints); $i < $l; $i++) {
144
                $right = $constraints[$i];
145
                if ($left instanceof self && $left->conjunctive && $right instanceof self && $right->conjunctive && \count($left->constraints) === 2 && \count($right->constraints) === 2 && ($left0 = (string) $left->constraints[0]) && $left0[0] === '>' && $left0[1] === '=' && ($left1 = (string) $left->constraints[1]) && $left1[0] === '<' && ($right0 = (string) $right->constraints[0]) && $right0[0] === '>' && $right0[1] === '=' && ($right1 = (string) $right->constraints[1]) && $right1[0] === '<' && \substr($left1, 2) === \substr($right0, 3)) {
146
                    $optimized = \true;
147
                    $left = new MultiConstraint(array($left->constraints[0], $right->constraints[1]), \true);
148
                } else {
149
                    $mergedConstraints[] = $left;
150
                    $left = $right;
151
                }
152
            }
153
            if ($optimized) {
154
                $mergedConstraints[] = $left;
155
                return array($mergedConstraints, \false);
156
            }
157
        }
158
        return null;
159
    }
160
    private function extractBounds()
161
    {
162
        if (null !== $this->lowerBound) {
163
            return;
164
        }
165
        foreach ($this->constraints as $constraint) {
166
            if (null === $this->lowerBound || null === $this->upperBound) {
167
                $this->lowerBound = $constraint->getLowerBound();
168
                $this->upperBound = $constraint->getUpperBound();
169
                continue;
170
            }
171
            if ($constraint->getLowerBound()->compareTo($this->lowerBound, $this->isConjunctive() ? '>' : '<')) {
172
                $this->lowerBound = $constraint->getLowerBound();
173
            }
174
            if ($constraint->getUpperBound()->compareTo($this->upperBound, $this->isConjunctive() ? '<' : '>')) {
175
                $this->upperBound = $constraint->getUpperBound();
176
            }
177
        }
178
    }
179
}
180