Passed
Push — master ( 9d8a75...f0a095 )
by Alexander
01:39
created

CompareTo::asNumber()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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