Passed
Pull Request — master (#422)
by Alexander
03:36 queued 01:01
created

LimitTrait::getMin()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
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
 *     int|float|null $min = null,
18
 *     int|float|null $max = null,
19
 *     int|float|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 int|float|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 int|float|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 int|float|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
     */
100
    private function initLimitProperties(
101
        int|float|null $min,
102
        int|float|null $max,
103
        int|float|null $exactly,
104
        string $lessThanMinMessage,
105
        string $greaterThanMinMessage,
106
        string $notExactlyMessage,
107
        bool $requireLimits = true,
108 91
        bool $allowNegativeLimits = false,
109
    ): void {
110 91
        $this->min = $min;
111
        $this->max = $max;
112
        $this->exactly = $exactly;
113
        $this->lessThanMinMessage = $lessThanMinMessage;
114
        $this->greaterThanMaxMessage = $greaterThanMinMessage;
115
        $this->notExactlyMessage = $notExactlyMessage;
116
117
        if ($this->min === null && $this->max === null && $this->exactly === null) {
118 91
            if ($requireLimits === false) {
119
                return;
120 91
            }
121
122
            $message = 'At least one of these attributes must be specified: $min, $max, $exactly.';
123
124
            throw new InvalidArgumentException($message);
125
        }
126
127
        if (($this->min !== null || $this->max !== null) && $this->exactly !== null) {
128 99
            throw new InvalidArgumentException('$exactly is mutually exclusive with $min and $max.');
129
        }
130 99
131
        if (
132
            $allowNegativeLimits === false &&
133
            (
134
                ($this->min !== null && $this->min <= 0) ||
135
                ($this->max !== null && $this->max <= 0) ||
136
                ($this->exactly !== null && $this->exactly <= 0)
137
            )
138 48
        ) {
139
            throw new InvalidArgumentException('Only positive values are allowed.');
140 48
        }
141
142
        if ($this->min !== null && $this->max !== null) {
143
            if ($this->min > $this->max) {
144
                throw new InvalidArgumentException('$min must be lower than $max.');
145
            }
146
147
            if ($this->min === $this->max) {
148 9
                throw new InvalidArgumentException('Use $exactly instead.');
149
            }
150 9
        }
151
    }
152
153
    /**
154
     * A getter for {@see $min} property.
155
     *
156
     * @return float|int|null A number representing minimum boundary. `null` means no lower bound.
157
     */
158 8
    public function getMin(): int|float|null
159
    {
160 8
        return $this->min;
161
    }
162
163
    /**
164
     * A getter for {@see $max property}.
165
     *
166 6
     * @return float|int|null A number representing maximum boundary. `null` means no upper bound.
167
     */
168
    public function getMax(): int|float|null
169
    {
170
        return $this->max;
171
    }
172
173
    /**
174
     * A getter for {@see $exactly} property.
175
     *
176
     * @return float|int|null A number representing "exactly" value. `null` means no strict comparison so lower / upper limits /
177 6
     * both must be set.
178 6
     */
179 6
    public function getExactly(): int|float|null
180
    {
181 6
        return $this->exactly;
182 6
    }
183
184
    /**
185 6
     * A getter for {@see $lessThanMinMessage} property.
186 6
     *
187
     * @return string Validation error message.
188
     */
189 6
    public function getLessThanMinMessage(): string
190 6
    {
191
        return $this->lessThanMinMessage;
192
    }
193
194
    /**
195
     * A getter for {@see $greaterThanMaxMessage} property.
196
     *
197
     * @return string Validation error message.
198
     */
199
    public function getGreaterThanMaxMessage(): string
200
    {
201
        return $this->greaterThanMaxMessage;
202
    }
203
204
    /**
205
     * A getter for {@see $notExactlyMessage} property.
206
     *
207
     * @return string Validation error message.
208
     */
209
    public function getNotExactlyMessage(): string
210
    {
211
        return $this->notExactlyMessage;
212
    }
213
214
    /**
215
     * Limit related options intended to be merged with other rule options.
216
     *
217
     * @return array<string, mixed> A map between property name and property value.
218
     */
219
    #[ArrayShape([
220
        'min' => 'int|null',
221
        'max' => 'int|null',
222
        'exactly' => 'int|null',
223
        'lessThanMinMessage' => 'array',
224
        'greaterThanMaxMessage' => 'array',
225
        'notExactlyMessage' => 'array',
226
    ])]
227
    private function getLimitOptions(): array
228
    {
229
        return [
230
            'min' => $this->min,
231
            'max' => $this->max,
232
            'exactly' => $this->exactly,
233
            'lessThanMinMessage' => [
234
                'template' => $this->lessThanMinMessage,
235
                'parameters' => ['min' => $this->min],
236
            ],
237
            'greaterThanMaxMessage' => [
238
                'template' => $this->greaterThanMaxMessage,
239
                'parameters' => ['max' => $this->max],
240
            ],
241
            'notExactlyMessage' => [
242
                'template' => $this->notExactlyMessage,
243
                'parameters' => ['exactly' => $this->exactly],
244
            ],
245
        ];
246
    }
247
}
248