LogicFormulaValidator::validate()   D
last analyzed

Complexity

Conditions 25
Paths 38

Size

Total Lines 78
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 56
CRAP Score 25

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 52
c 2
b 1
f 0
dl 0
loc 78
ccs 56
cts 56
cp 1
rs 4.1666
cc 25
nc 38
nop 1
crap 25

How to fix   Long Method    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
declare(strict_types=1);
4
5
namespace Smoren\FormulaTools\Validators;
6
7
use InvalidArgumentException;
8
use Smoren\FormulaTools\Exceptions\BracketsException;
9
use Smoren\FormulaTools\Exceptions\InappropriateTokenException;
10
use Smoren\FormulaTools\Exceptions\InappropriateTokenPairException;
11
use Smoren\FormulaTools\Exceptions\InvalidTokenException;
12
use Smoren\FormulaTools\Exceptions\SyntaxException;
13
use Smoren\FormulaTools\Helpers\LoopHelper;
14
15
class LogicFormulaValidator
16
{
17
    /**
18
     * @var array<string>
19
     */
20
    protected array $unaryOperators;
21
    /**
22
     * @var array<string>
23
     */
24
    protected array $binaryOperators;
25
26
    /**
27
     * @param array<string> $unaryOperators
28
     * @param array<string> $binaryOperators
29
     */
30 210
    public function __construct(array $unaryOperators, array $binaryOperators)
31
    {
32 210
        $this->unaryOperators = $unaryOperators;
33 210
        $this->binaryOperators = $binaryOperators;
34
    }
35
36
    /**
37
     * @param array<string> $tokens
38
     * @return void
39
     *
40
     * @throws InvalidArgumentException
41
     * @throws SyntaxException
42
     */
43 210
    public function validate(array $tokens): void
44
    {
45 210
        if (!count($tokens)) {
46 2
            return;
47
        }
48
49 208
        $bracketsCount = 0;
50
51 208
        foreach ($tokens as $token) {
52 208
            if (!is_string($token)) {
53 22
                throw new InvalidArgumentException("Token must be string");
54
            }
55
56
            switch (true) {
57 190
                case $this->isOpeningBracket($token):
58 126
                    $bracketsCount++;
59 126
                    break;
60 188
                case $this->isClosingBracket($token):
61 130
                    $bracketsCount--;
62 130
                    break;
63 166
                case !$this->isValidToken($token):
64 4
                    throw new InvalidTokenException("Token is invalid", $token);
65
            }
66
67 188
            if ($bracketsCount < 0) {
68 22
                throw new BracketsException("Brackets error", $token);
69
            }
70
        }
71
72 160
        $lastToken = $tokens[count($tokens) - 1];
73
74 160
        if ($bracketsCount !== 0) {
75 6
            throw new BracketsException(
76 6
                "Brackets error",
77 6
                $lastToken
78 6
            );
79
        }
80
81 154
        if ($this->isOperator($lastToken)) {
82 12
            throw new InappropriateTokenException(
83 12
                "The last token '{$lastToken}' cannot be operator",
84 12
                $lastToken
85 12
            );
86
        }
87
88
        /**
89
         * @var string $lhs
90
         * @var string $rhs
91
         */
92 142
        foreach (LoopHelper::pairwise($tokens) as [$lhs, $rhs]) {
93
            switch (true) {
94 140
                case $this->isOperand($lhs)
95 140
                    && ($this->isOperand($rhs) || $this->isOpeningBracket($rhs) || $this->isUnaryOperator($rhs)):
96 30
                    throw new InappropriateTokenPairException(
97 30
                        "Inappropriate token '{$rhs}' after operand '{$lhs}'",
98 30
                        $rhs,
99 30
                        $lhs
100 30
                    );
101 130
                case $this->isOpeningBracket($lhs)
102 130
                    && ($this->isClosingBracket($rhs) || $this->isBinaryOperator($rhs)):
103 20
                    throw new InappropriateTokenPairException(
104 20
                        "Inappropriate token '{$rhs}' after opening bracket",
105 20
                        $rhs,
106 20
                        $lhs
107 20
                    );
108 120
                case $this->isClosingBracket($lhs)
109 120
                    && ($this->isOpeningBracket($rhs) || $this->isOperand($rhs) || $this->isUnaryOperator($rhs)):
110 22
                    throw new InappropriateTokenPairException(
111 22
                        "Inappropriate token '{$rhs}' after closing bracket",
112 22
                        $rhs,
113 22
                        $lhs
114 22
                    );
115 120
                case $this->isOperator($lhs)
116 120
                    && ($this->isBinaryOperator($rhs) || $this->isClosingBracket($rhs)):
117 28
                    throw new InappropriateTokenPairException(
118 28
                        "Inappropriate token '{$rhs}' after operator '{$lhs}'",
119 28
                        $rhs,
120 28
                        $lhs
121 28
                    );
122
            }
123
        }
124
    }
125
126 170
    protected function isBracket(string $token): bool
127
    {
128 170
        return $this->isOpeningBracket($token) || $this->isClosingBracket($token);
129
    }
130
131 190
    protected function isOpeningBracket(string $token): bool
132
    {
133 190
        return $token === '(';
134
    }
135
136 188
    protected function isClosingBracket(string $token): bool
137
    {
138 188
        return $token === ')';
139
    }
140
141 154
    protected function isOperator(string $token): bool
142
    {
143 154
        return $this->isUnaryOperator($token) || $this->isBinaryOperator($token);
144
    }
145
146 154
    protected function isUnaryOperator(string $token): bool
147
    {
148 154
        return in_array($token, $this->unaryOperators);
149
    }
150
151 150
    protected function isBinaryOperator(string $token): bool
152
    {
153 150
        return in_array($token, $this->binaryOperators);
154
    }
155
156 140
    protected function isOperand(string $token): bool
157
    {
158 140
        return !$this->isBracket($token) && !$this->isOperator($token);
159
    }
160
161 166
    private function isValidToken(string $token): bool
162
    {
163 166
        return $this->isBracket($token) || !preg_match('/[\(\)]/', $token);
164
    }
165
}
166