Passed
Pull Request — master (#219)
by
unknown
02:35
created

CompareTo::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 37
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 8
dl 0
loc 37
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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