Passed
Pull Request — master (#422)
by
unknown
02:32
created

LimitTrait::getMin()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule\Trait;
6
7
use InvalidArgumentException;
8
use JetBrains\PhpStorm\ArrayShape;
9
10
/**
11
 * An implementation for {@see LimitInterface} intended to be included in rules. The following arguments need to be
12
 * added in constructor and passed with {@see initLimitProperties()} call:
13
 *
14
 * ```php
15
 * public function __construct(
16
 *     // ...
17
 *     float|int|null $min = null,
18
 *     float|int|null $max = null,
19
 *     float|int|null $exactly = null,
20
 *     string $lessThanMinMessage = 'Less than {min}.',
21
 *     string $greaterThanMinMessage = 'Greater than {max}.',
22
 *     string $greaterThanMinMessage = 'Not exactly {exactly}.',
23
 *     // ...
24
 * ) {
25
 *     // ...
26
 *     $this->initLimitProperties(
27
 *         $min,
28
 *         $max,
29
 *         $exactly,
30
 *         $lessThanMinMessage,
31
 *         $greaterThanMaxMessage,
32
 *         $notExactlyMessage,
33
 *     );
34
 *     // ...
35
 * }
36
 * ```
37
 *
38
 * Also, if a rule implements {@see RuleWithOptionsInterface}, you can merge limit related options instead of adding it
39
 * manually:
40
 *
41
 * ```php
42
 * public function getOptions(): array
43
 * {
44
 *     return array_merge($this->getLimitOptions(), [
45
 *         // Other rule options.
46
 *     ]);
47
 * }
48
 * ```
49
 *
50
 * Make sure to include {@see LimitHandlerTrait} in according handler as well.
51
 */
52
trait LimitTrait
53
{
54
    /**
55
     * @var float|int|null Minimum limit. Can't be combined with {@see $exactly}.
56
     *
57
     * @see $lessThanMinMessage for related error message.
58
     */
59 52
    private float|int|null $min = null;
60
    /**
61
     * @var float|int|null Maximum limit. Can't be combined with {@see $exactly}.
62
     *
63
     * @see $greaterThanMaxMessage for related error message.
64
     */
65
    private float|int|null $max = null;
66
    /**
67 52
     * @var float|int|null "Exactly" number. A shortcut / replacement for the case when {@see $min} and {@see $max} have the
68 52
     * same not-null value. Mutually exclusive with both {@see $min} and {@see $max}. `null` means no strict comparison
69 52
     * so lower / upper limits / both must be set.
70 52
     *
71 52
     * @see $notExactlyMessage for related error message.
72 52
     */
73
    private float|int|null $exactly = null;
74 52
    /**
75 2
     * @var string Validation error message used when a validated value is less than minimum set in {@see $min}.
76
     */
77
    private string $lessThanMinMessage;
78
    /**
79
     * @var string Validation error message used when a validated value is greater than maximum set in {@see $max}.
80 50
     */
81 6
    private string $greaterThanMaxMessage;
82
    /**
83
     * @var string Validation error message used when a validated value doesn't exactly match the one set in
84
     * {@see $exactly}.
85 44
     */
86 34
    private string $notExactlyMessage;
87 44
88
    /**
89 18
     * Initializes limit related properties and runs checks for required, mutually exclusive properties and their
90
     * allowed values (including dependency on each other).
91
     *
92 26
     * @param float|int|null $min Minimum limit ({@see $min}).
93 4
     * @param float|int|null $max Maximum limit ({@see $max}).
94 2
     * @param float|int|null $exactly "Exactly" number ({@see $exactly}).
95
     * @param string $lessThanMinMessage "Less than minimum" validation error message ({@see $lessThanMinMessage}).
96
     * @param string $greaterThanMinMessage "Greater than maximum" validation error message
97 2
     * ({@see $greaterThanMinMessage}).
98 2
     * @param string $notExactlyMessage "Not exactly" validation error message ({@see $notExactlyMessage}).
99
     * @param bool $requireLimits
100
     * @param bool $allowNegativeLimits
101
     */
102
    private function initLimitProperties(
103
        float|int|null $min,
104
        float|int|null $max,
105
        float|int|null $exactly,
106
        string $lessThanMinMessage,
107
        string $greaterThanMinMessage,
108 91
        string $notExactlyMessage,
109
        bool $requireLimits = true,
110 91
        bool $allowNegativeLimits = false,
111
    ): void {
112
        $this->min = $min;
113
        $this->max = $max;
114
        $this->exactly = $exactly;
115
        $this->lessThanMinMessage = $lessThanMinMessage;
116
        $this->greaterThanMaxMessage = $greaterThanMinMessage;
117
        $this->notExactlyMessage = $notExactlyMessage;
118 91
119
        if ($this->min === null && $this->max === null && $this->exactly === null) {
120 91
            if ($requireLimits === false) {
121
                return;
122
            }
123
124
            $message = 'At least one of these attributes must be specified: $min, $max, $exactly.';
125
126
            throw new InvalidArgumentException($message);
127
        }
128 99
129
        if (($this->min !== null || $this->max !== null) && $this->exactly !== null) {
130 99
            throw new InvalidArgumentException('$exactly is mutually exclusive with $min and $max.');
131
        }
132
133
        if (
134
            $allowNegativeLimits === false &&
135
            (
136
                ($this->min !== null && $this->min <= 0) ||
137
                ($this->max !== null && $this->max <= 0) ||
138 48
                ($this->exactly !== null && $this->exactly <= 0)
139
            )
140 48
        ) {
141
            throw new InvalidArgumentException('Only positive values are allowed.');
142
        }
143
144
        if ($this->min !== null && $this->max !== null) {
145
            if ($this->min > $this->max) {
146
                throw new InvalidArgumentException('$min must be lower than $max.');
147
            }
148 9
149
            if ($this->min === $this->max) {
150 9
                throw new InvalidArgumentException('Use $exactly instead.');
151
            }
152
        }
153
    }
154
155
    /**
156
     * A getter for {@see $min} property.
157
     *
158 8
     * @return float|int|null A number representing minimum boundary. `null` means no lower bound.
159
     */
160 8
    public function getMin(): float|int|null
161
    {
162
        return $this->min;
163
    }
164
165
    /**
166 6
     * A getter for {@see $max property}.
167
     *
168
     * @return float|int|null A number representing maximum boundary. `null` means no upper bound.
169
     */
170
    public function getMax(): float|int|null
171
    {
172
        return $this->max;
173
    }
174
175
    /**
176
     * A getter for {@see $exactly} property.
177 6
     *
178 6
     * @return float|int|null A number representing "exactly" value. `null` means no strict comparison so lower / upper limits /
179 6
     * both must be set.
180
     */
181 6
    public function getExactly(): float|int|null
182 6
    {
183
        return $this->exactly;
184
    }
185 6
186 6
    /**
187
     * A getter for {@see $lessThanMinMessage} property.
188
     *
189 6
     * @return string Validation error message.
190 6
     */
191
    public function getLessThanMinMessage(): string
192
    {
193
        return $this->lessThanMinMessage;
194
    }
195
196
    /**
197
     * A getter for {@see $greaterThanMaxMessage} property.
198
     *
199
     * @return string Validation error message.
200
     */
201
    public function getGreaterThanMaxMessage(): string
202
    {
203
        return $this->greaterThanMaxMessage;
204
    }
205
206
    /**
207
     * A getter for {@see $notExactlyMessage} property.
208
     *
209
     * @return string Validation error message.
210
     */
211
    public function getNotExactlyMessage(): string
212
    {
213
        return $this->notExactlyMessage;
214
    }
215
216
    /**
217
     * Limit related options intended to be merged with other rule options.
218
     *
219
     * @return array<string, mixed> A map between property name and property value.
220
     */
221
    #[ArrayShape([
222
        'min' => 'int|null',
223
        'max' => 'int|null',
224
        'exactly' => 'int|null',
225
        'lessThanMinMessage' => 'array',
226
        'greaterThanMaxMessage' => 'array',
227
        'notExactlyMessage' => 'array',
228
    ])]
229
    private function getLimitOptions(): array
230
    {
231
        return [
232
            'min' => $this->min,
233
            'max' => $this->max,
234
            'exactly' => $this->exactly,
235
            'lessThanMinMessage' => [
236
                'template' => $this->lessThanMinMessage,
237
                'parameters' => ['min' => $this->min],
238
            ],
239
            'greaterThanMaxMessage' => [
240
                'template' => $this->greaterThanMaxMessage,
241
                'parameters' => ['max' => $this->max],
242
            ],
243
            'notExactlyMessage' => [
244
                'template' => $this->notExactlyMessage,
245
                'parameters' => ['exactly' => $this->exactly],
246
            ],
247
        ];
248
    }
249
}
250