Passed
Push — master ( b36446...0e452c )
by Alexander
12:51 queued 10:34
created

CompareTo::getMessage()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 23
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 10.0145

Importance

Changes 0
Metric Value
cc 10
eloc 19
nc 10
nop 0
dl 0
loc 23
ccs 18
cts 19
cp 0.9474
crap 10.0145
rs 7.6666
c 0
b 0
f 0

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
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use Yiisoft\Validator\HasValidationErrorMessage;
8
use Yiisoft\Validator\Result;
9
use Yiisoft\Validator\Rule;
10
use Yiisoft\Validator\ValidationContext;
11
12
/**
13
 * CompareValidator compares the specified attribute value with another value.
14
 *
15
 * The value being compared with a constant {@see CompareTo::$compareValue}, which is set
16
 * in the constructor.
17
 *
18
 * CompareValidator supports different comparison operators, specified
19
 * via the {@see CompareTo::operator()}.
20
 *
21
 * The default comparison function is based on string values, which means the values
22
 * are compared byte by byte. When comparing numbers, make sure to call the {@see CompareTo::asNumber()}
23
 * to enable numeric comparison.
24
 */
25
class CompareTo extends Rule
26
{
27
    use HasValidationErrorMessage;
28
29
    /**
30
     * Constant for specifying the comparison as string values.
31
     * No conversion will be done before comparison.
32
     *
33
     * @see type
34
     */
35
    private const TYPE_STRING = 'string';
36
    /**
37
     * Constant for specifying the comparison as numeric values.
38
     * String values will be converted into numbers before comparison.
39
     *
40
     * @see type
41
     */
42
    private const TYPE_NUMBER = 'number';
43
44
    /**
45
     * @var mixed the constant value to be compared with.
46
     */
47
    private $compareValue;
48
49
    /**
50
     * @var ?string user-defined error message
51
     */
52
    private ?string $message = null;
53
54
    /**
55
     * @var string the type of the values being compared.
56
     */
57
    private string $type = self::TYPE_STRING;
58
    /**
59
     * @var string the operator for comparison. The following operators are supported:
60
     *
61
     * - `==`: check if two values are equal. The comparison is done is non-strict mode.
62
     * - `===`: check if two values are equal. The comparison is done is strict mode.
63
     * - `!=`: check if two values are NOT equal. The comparison is done is non-strict mode.
64
     * - `!==`: check if two values are NOT equal. The comparison is done is strict mode.
65
     * - `>`: check if value being validated is greater than the value being compared with.
66
     * - `>=`: check if value being validated is greater than or equal to the value being compared with.
67
     * - `<`: check if value being validated is less than the value being compared with.
68
     * - `<=`: check if value being validated is less than or equal to the value being compared with.
69
     *
70
     * When you want to compare numbers, make sure to also call {@see asNumber()}.
71
     */
72
    private string $operator = '==';
73
74
    private array $validOperators = [
75
        '==' => 1,
76
        '===' => 1,
77
        '!=' => 1,
78
        '!==' => 1,
79
        '>' => 1,
80
        '>=' => 1,
81
        '<' => 1,
82
        '<=' => 1,
83
    ];
84
85 8
    private function getMessage(): string
86
    {
87 8
        if ($this->message !== null) {
88 1
            return $this->message;
89
        }
90
91 7
        switch ($this->operator) {
92 7
            case '==':
93 3
            case '===':
94 5
                return 'Value must be equal to "{value}".';
95 3
            case '!=':
96 3
            case '!==':
97 2
                return 'Value must not be equal to "{value}".';
98 2
            case '>':
99 1
                return 'Value must be greater than "{value}".';
100 2
            case '>=':
101 2
                return 'Value must be greater than or equal to "{value}".';
102 1
            case '<':
103 1
                return 'Value must be less than "{value}".';
104 1
            case '<=':
105 1
                return 'Value must be less than or equal to "{value}".';
106
            default:
107
                throw new \RuntimeException("Unknown operator: {$this->operator}");
108
        }
109
    }
110
111 3
    public function __construct($value)
112
    {
113 3
        $this->compareValue = $value;
114 3
    }
115
116 1
    public function operator(string $operator): self
117
    {
118 1
        if (!isset($this->validOperators[$operator])) {
119
            throw new \InvalidArgumentException("Operator \"$operator\" is not supported.");
120
        }
121
122 1
        $new = clone $this;
123 1
        $new->operator = $operator;
124 1
        return $new;
125
    }
126
127
    public function asString(): self
128
    {
129
        $new = clone $this;
130
        $new->type = self::TYPE_STRING;
131
        return $new;
132
    }
133
134
    public function asNumber(): self
135
    {
136
        $new = clone $this;
137
        $new->type = self::TYPE_NUMBER;
138
        return $new;
139
    }
140
141 2
    protected function validateValue($value, ValidationContext $context = null): Result
142
    {
143 2
        $result = new Result();
144
145 2
        if (!$this->compareValues($this->operator, $this->type, $value, $this->compareValue)) {
146 1
            $result->addError(
147 1
                $this->formatMessage(
148 1
                    $this->getMessage(),
149
                    [
150 1
                        'value' => $this->compareValue,
151
                    ]
152
                )
153
            );
154
        }
155
156 2
        return $result;
157
    }
158
159
    /**
160
     * Compares two values with the specified operator.
161
     *
162
     * @param string $operator the comparison operator
163
     * @param string $type the type of the values being compared
164
     * @param mixed $value the value being compared
165
     * @param mixed $compareValue another value being compared
166
     *
167
     * @return bool whether the comparison using the specified operator is true.
168
     */
169 2
    protected function compareValues(string $operator, string $type, $value, $compareValue): bool
170
    {
171 2
        if ($type === self::TYPE_NUMBER) {
172
            $value = (float)$value;
173
            $compareValue = (float)$compareValue;
174
        } else {
175 2
            $value = (string)$value;
176 2
            $compareValue = (string)$compareValue;
177
        }
178 2
        switch ($operator) {
179 2
            case '==':
180 2
                return $value == $compareValue;
181 1
            case '===':
182 1
                return $value === $compareValue;
183 1
            case '!=':
184 1
                return $value != $compareValue;
185 1
            case '!==':
186 1
                return $value !== $compareValue;
187 1
            case '>':
188 1
                return $value > $compareValue;
189 1
            case '>=':
190 1
                return $value >= $compareValue;
191 1
            case '<':
192 1
                return $value < $compareValue;
193 1
            case '<=':
194 1
                return $value <= $compareValue;
195
            default:
196
                return false;
197
        }
198
    }
199
200 7
    public function getOptions(): array
201
    {
202 7
        return array_merge(
203 7
            parent::getOptions(),
204
            [
205 7
                'type' => $this->type,
206 7
                'operator' => $this->operator,
207 7
                'compareValue' => $this->compareValue,
208 7
                'message' => $this->formatMessage($this->getMessage(), ['value' => $this->compareValue]),
209
            ],
210
        );
211
    }
212
}
213