Passed
Pull Request — master (#586)
by
unknown
03:06 queued 18s
created

CountableLimitTrait::getLimitOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 20
nc 1
nop 0
dl 0
loc 25
rs 9.6
c 0
b 0
f 0
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 CountableLimitInterface} intended to be included in rules. The following arguments need
12
 * to be added in constructor and passed with {@see initLimitProperties()} call:
13
 *
14
 * ```php
15
 * public function __construct(
16
 *     // ...
17
 *     int|null $min = null,
18
 *     int|null $max = null,
19
 *     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 CountableLimitHandlerTrait} in according handler as well.
51
 */
52
trait CountableLimitTrait
53
{
54
    /**
55
     * @var int|null Minimum limit. Can't be combined with {@see $exactly}.
56
     *
57
     * @see $lessThanMinMessage for related error message.
58
     */
59
    private int|null $min = null;
60
    /**
61
     * @var int|null Maximum limit. Can't be combined with {@see $exactly}.
62
     *
63
     * @see $greaterThanMaxMessage for related error message.
64
     */
65
    private int|null $max = null;
66
    /**
67
     * @var int|null "Exactly" number. A shortcut / replacement for the case when {@see $min} and {@see $max} have the
68
     * same not-null value. Mutually exclusive with both {@see $min} and {@see $max}. `null` means no strict comparison
69
     * so lower / upper limits / both must be set.
70
     *
71
     * @see $notExactlyMessage for related error message.
72
     */
73
    private int|null $exactly = null;
74
    /**
75
     * @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
     */
81
    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
     */
86
    private string $notExactlyMessage;
87
88
    /**
89
     * Initializes countable limit related properties and runs checks for required, mutually exclusive properties and
90
     * their allowed values (including dependency on each other).
91
     *
92
     * @param int|null $min Minimum limit ({@see $min}).
93
     * @param int|null $max Maximum limit ({@see $max}).
94
     * @param 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
     * ({@see $greaterThanMinMessage}).
98
     * @param string $notExactlyMessage "Not exactly" validation error message ({@see $notExactlyMessage}).
99
     */
100
    private function initCountableLimitProperties(
101
        int|null $min,
102
        int|null $max,
103
        int|null $exactly,
104
        string $lessThanMinMessage,
105
        string $greaterThanMinMessage,
106
        string $notExactlyMessage
107
    ): void {
108
        $this->min = $min;
109
        $this->max = $max;
110
        $this->exactly = $exactly;
111
        $this->lessThanMinMessage = $lessThanMinMessage;
112
        $this->greaterThanMaxMessage = $greaterThanMinMessage;
113
        $this->notExactlyMessage = $notExactlyMessage;
114
115
        if ($this->min === null && $this->max === null && $this->exactly === null) {
116
            throw new InvalidArgumentException(
117
                'At least one of these attributes must be specified: $min, $max, $exactly.'
118
            );
119
        }
120
121
        if (($this->min !== null || $this->max !== null) && $this->exactly !== null) {
122
            throw new InvalidArgumentException('$exactly is mutually exclusive with $min and $max.');
123
        }
124
125
        if (
126
            ($this->min !== null && $this->min < 0) ||
127
            ($this->max !== null && $this->max < 0) ||
128
            ($this->exactly !== null && $this->exactly < 0)
129
        ) {
130
            throw new InvalidArgumentException('Only positive or zero values are allowed.');
131
        }
132
133
        $messageForUsingExactly = 'Use $exactly instead.';
134
135
        if ($this->min !== null && $this->max !== null) {
136
            if ($this->min > $this->max) {
137
                throw new InvalidArgumentException('$min must be lower than $max.');
138
            }
139
140
            if ($this->min === $this->max) {
141
                throw new InvalidArgumentException($messageForUsingExactly);
142
            }
143
        } elseif ($this->min === null && $this->max === 0) {
144
            throw new InvalidArgumentException($messageForUsingExactly);
145
        }
146
    }
147
148
    /**
149
     * A getter for {@see $min} property.
150
     *
151
     * @return int|null A number representing minimum boundary. `null` means no lower bound.
152
     */
153
    public function getMin(): int|null
154
    {
155
        return $this->min;
156
    }
157
158
    /**
159
     * A getter for {@see $max property}.
160
     *
161
     * @return int|null A number representing maximum boundary. `null` means no upper bound.
162
     */
163
    public function getMax(): int|null
164
    {
165
        return $this->max;
166
    }
167
168
    /**
169
     * A getter for {@see $exactly} property.
170
     *
171
     * @return int|null A number representing "exactly" value. `null` means no strict comparison so lower / upper limits /
172
     * both must be set.
173
     */
174
    public function getExactly(): int|null
175
    {
176
        return $this->exactly;
177
    }
178
179
    /**
180
     * A getter for {@see $lessThanMinMessage} property.
181
     *
182
     * @return string Validation error message.
183
     */
184
    public function getLessThanMinMessage(): string
185
    {
186
        return $this->lessThanMinMessage;
187
    }
188
189
    /**
190
     * A getter for {@see $greaterThanMaxMessage} property.
191
     *
192
     * @return string Validation error message.
193
     */
194
    public function getGreaterThanMaxMessage(): string
195
    {
196
        return $this->greaterThanMaxMessage;
197
    }
198
199
    /**
200
     * A getter for {@see $notExactlyMessage} property.
201
     *
202
     * @return string Validation error message.
203
     */
204
    public function getNotExactlyMessage(): string
205
    {
206
        return $this->notExactlyMessage;
207
    }
208
209
    /**
210
     * Limit related options intended to be merged with other rule options.
211
     *
212
     * @return array<string, mixed> A map between property name and property value.
213
     */
214
    #[ArrayShape([
215
        'min' => 'int|null',
216
        'max' => 'int|null',
217
        'exactly' => 'int|null',
218
        'lessThanMinMessage' => 'array',
219
        'greaterThanMaxMessage' => 'array',
220
        'notExactlyMessage' => 'array',
221
    ])]
222
    private function getLimitOptions(): array
223
    {
224
        return [
225
            'min' => $this->min,
226
            'max' => $this->max,
227
            'exactly' => $this->exactly,
228
            'lessThanMinMessage' => [
229
                'template' => $this->lessThanMinMessage,
230
                'parameters' => ['min' => $this->min],
231
            ],
232
            'greaterThanMaxMessage' => [
233
                'template' => $this->greaterThanMaxMessage,
234
                'parameters' => ['max' => $this->max],
235
            ],
236
            'notExactlyMessage' => [
237
                'template' => $this->notExactlyMessage,
238
                'parameters' => ['exactly' => $this->exactly],
239
            ],
240
        ];
241
    }
242
}
243