Test Setup Failed
Push — master ( e94fe5...b7ec94 )
by
unknown
02:01
created

CompareTo::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 38
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

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

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
    public function __construct(
57
        /**
58
         * @var mixed the constant value to be compared with.
59
         */
60
        private $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 8
        ?FormatterInterface $formatter = null,
86
        bool $skipOnEmpty = false,
87 8
        bool $skipOnError = false,
88 1
        $when = null
89
    ) {
90
        parent::__construct(formatter: $formatter, skipOnEmpty: $skipOnEmpty, skipOnError: $skipOnError, when: $when);
91 7
92 7
        if (!isset($this->validOperators[$operator])) {
93 3
            throw new InvalidArgumentException("Operator \"$operator\" is not supported.");
94 5
        }
95 3
    }
96 3
97 2
    private function getMessage(): string
98 2
    {
99 1
        if ($this->message !== null) {
100 2
            return $this->message;
101 2
        }
102 1
103 1
        switch ($this->operator) {
104 1
            case '==':
105 1
            case '===':
106
                return 'Value must be equal to "{value}".';
107
            case '!=':
108
            case '!==':
109
                return 'Value must not be equal to "{value}".';
110
            case '>':
111 3
                return 'Value must be greater than "{value}".';
112
            case '>=':
113 3
                return 'Value must be greater than or equal to "{value}".';
114 3
            case '<':
115 3
                return 'Value must be less than "{value}".';
116
            case '<=':
117
                return 'Value must be less than or equal to "{value}".';
118 1
            default:
119
                throw new RuntimeException("Unknown operator: {$this->operator}");
120 1
        }
121
    }
122
123
    protected function validateValue($value, ?ValidationContext $context = null): Result
124 1
    {
125 1
        $result = new Result();
126 1
127
        if (!$this->compareValues($this->operator, $this->type, $value, $this->compareValue)) {
128
            $message = $this->formatMessage($this->getMessage(), ['value' => $this->compareValue]);
129
            $result->addError($message);
130
        }
131
132
        return $result;
133
    }
134
135
    /**
136
     * Compares two values with the specified operator.
137
     *
138
     * @param string $operator the comparison operator
139
     * @param string $type the type of the values being compared
140
     * @param mixed $value the value being compared
141
     * @param mixed $compareValue another value being compared
142
     *
143 2
     * @return bool whether the comparison using the specified operator is true.
144
     */
145 2
    protected function compareValues(string $operator, string $type, $value, $compareValue): bool
146
    {
147 2
        if ($type === self::TYPE_NUMBER) {
148 1
            $value = (float) $value;
149 1
            $compareValue = (float)$compareValue;
150 1
        } else {
151
            $value = (string) $value;
152 1
            $compareValue = (string) $compareValue;
153
        }
154
        switch ($operator) {
155
            case '==':
156
                return $value == $compareValue;
157
            case '===':
158 2
                return $value === $compareValue;
159
            case '!=':
160
                return $value != $compareValue;
161
            case '!==':
162
                return $value !== $compareValue;
163
            case '>':
164
                return $value > $compareValue;
165
            case '>=':
166
                return $value >= $compareValue;
167
            case '<':
168
                return $value < $compareValue;
169
            case '<=':
170
                return $value <= $compareValue;
171 2
            default:
172
                return false;
173 2
        }
174
    }
175
176
    public function getOptions(): array
177 2
    {
178 2
        return array_merge(parent::getOptions(), [
179
            'compareValue' => $this->compareValue,
180 2
            'message' => $this->formatMessage($this->getMessage(), ['value' => $this->compareValue]),
181 2
            'type' => $this->type,
182 2
            'operator' => $this->operator,
183 1
        ]);
184 1
    }
185
}
186