Passed
Pull Request — master (#246)
by Alexander
04:26 queued 02:07
created

CompareTo::getOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

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