MultiConstraint   F
last analyzed

Complexity

Total Complexity 68

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 68
eloc 94
dl 0
loc 170
rs 2.96
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getLowerBound() 0 7 2
A getConstraints() 0 3 1
B matches() 0 19 8
A isDisjunctive() 0 3 1
A getPrettyString() 0 6 2
A getUpperBound() 0 7 2
B extractBounds() 0 16 9
A create() 0 16 5
A __toString() 0 10 4
A isConjunctive() 0 3 1
D optimizeConstraints() 0 22 21
B compile() 0 21 9
A setPrettyString() 0 3 1
A __construct() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like MultiConstraint often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MultiConstraint, and based on these observations, apply Extract Interface, too.

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