Passed
Push — master ( 89f940...819311 )
by Alexander
02:27
created

Count::getHandlerClassName()   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 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
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 Countable;
10
use InvalidArgumentException;
11
use JetBrains\PhpStorm\ArrayShape;
12
use Yiisoft\Validator\SerializableRuleInterface;
13
use Yiisoft\Validator\BeforeValidationInterface;
14
use Yiisoft\Validator\Rule\Trait\BeforeValidationTrait;
15
use Yiisoft\Validator\Rule\Trait\RuleNameTrait;
16
use Yiisoft\Validator\ValidationContext;
17
18
/**
19
 * Validates that the value contains certain number of items. Can be applied to arrays or classes implementing
20
 * {@see Countable} interface.
21
 */
22
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
23
final class Count implements SerializableRuleInterface, BeforeValidationInterface
24
{
25
    use BeforeValidationTrait;
26
    use RuleNameTrait;
27
28 7
    public function __construct(
29
        /**
30
         * @var int|null minimum number of items. null means no minimum number limit.
31
         *
32
         * @see $tooFewItemsMessage for the customized message for a value with too few items.
33
         */
34
        private ?int $min = null,
35
        /**
36
         * @var int|null maximum number of items. null means no maximum number limit.
37
         *
38
         * @see $tooManyItemsMessage for the customized message for a value wuth too many items.
39
         */
40
        private ?int $max = null,
41
        /**
42
         * @var int|null exact number of items. null means no strict comparison. Mutually exclusive with {@see $min} and
43
         * {@see $max}.
44
         */
45
        private ?int $exactly = null,
46
        /**
47
         * @var string user-defined error message used when the value is neither an array nor implementing
48
         * {@see \Countable} interface.
49
         *
50
         * @see Countable
51
         */
52
        private string $message = 'This value must be an array or implement \Countable interface.',
53
        /**
54
         * @var string user-defined error message used when the number of items is smaller than {@see $min}.
55
         */
56
        private string $tooFewItemsMessage = 'This value must contain at least {min, number} ' .
57
        '{min, plural, one{item} other{items}}.',
58
        /**
59
         * @var string user-defined error message used when the number of items is greater than {@see $max}.
60
         */
61
        private string $tooManyItemsMessage = 'This value must contain at most {max, number} ' .
62
        '{max, plural, one{item} other{items}}.',
63
        /**
64
         * @var string user-defined error message used when the number of items does not equal {@see $exactly}.
65
         */
66
        private string $notExactlyMessage = 'This value must contain exactly {max, number} ' .
67
        '{max, plural, one{item} other{items}}.',
68
        private bool $skipOnEmpty = false,
69
        private bool $skipOnError = false,
70
        /**
71
         * @var Closure(mixed, ValidationContext):bool|null
72
         */
73
        private ?Closure $when = null,
74
    ) {
75 7
        if (!$this->min && !$this->max && !$this->exactly) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->max of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $this->min of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $this->exactly of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
76 1
            throw new InvalidArgumentException(
77
                'At least one of these attributes must be specified: $min, $max, $exactly.'
78
            );
79
        }
80
81 6
        if ($this->exactly && ($this->min || $this->max)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->max of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $this->exactly of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $this->min of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
82 3
            throw new InvalidArgumentException('$exactly is mutually exclusive with $min and $max.');
83
        }
84
85 3
        if ($this->min && $this->max && $this->min === $this->max) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->max of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $this->min of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
86 1
            throw new InvalidArgumentException('Use $exactly instead.');
87
        }
88
    }
89
90
    /**
91
     * @return int|null
92
     */
93 18
    public function getMin(): ?int
94
    {
95 18
        return $this->min;
96
    }
97
98
    /**
99
     * @return int|null
100
     */
101 18
    public function getMax(): ?int
102
    {
103 18
        return $this->max;
104
    }
105
106
    /**
107
     * @return int|null
108
     */
109 19
    public function getExactly(): ?int
110
    {
111 19
        return $this->exactly;
112
    }
113
114
    /**
115
     * @return string
116
     */
117
    public function getMessage(): string
118
    {
119
        return $this->message;
120
    }
121
122
    /**
123
     * @return string
124
     */
125 9
    public function getTooFewItemsMessage(): string
126
    {
127 9
        return $this->tooFewItemsMessage;
128
    }
129
130
    /**
131
     * @return string
132
     */
133 2
    public function getTooManyItemsMessage(): string
134
    {
135 2
        return $this->tooManyItemsMessage;
136
    }
137
138
    /**
139
     * @return string
140
     */
141 1
    public function getNotExactlyMessage(): string
142
    {
143 1
        return $this->notExactlyMessage;
144
    }
145
146 1
    #[ArrayShape([
147
        'min' => 'int|null',
148
        'max' => 'int|null',
149
        'exactly' => 'int|null',
150
        'message' => 'string[]',
151
        'tooFewItemsMessage' => 'array',
152
        'tooManyItemsMessage' => 'array',
153
        'notExactlyMessage' => 'array',
154
        'skipOnEmpty' => 'bool',
155
        'skipOnError' => 'bool',
156
    ])]
157
    public function getOptions(): array
158
    {
159
        return [
160 1
            'min' => $this->min,
161 1
            'max' => $this->max,
162 1
            'exactly' => $this->exactly,
163
            'message' => [
164 1
                'message' => $this->message,
165
            ],
166
            'tooFewItemsMessage' => [
167 1
                'message' => $this->tooFewItemsMessage,
168 1
                'parameters' => ['min' => $this->min],
169
            ],
170
            'tooManyItemsMessage' => [
171 1
                'message' => $this->tooManyItemsMessage,
172 1
                'parameters' => ['max' => $this->max],
173
            ],
174
            'notExactlyMessage' => [
175 1
                'message' => $this->notExactlyMessage,
176 1
                'parameters' => ['exactly' => $this->exactly],
177
            ],
178 1
            'skipOnEmpty' => $this->skipOnEmpty,
179 1
            'skipOnError' => $this->skipOnError,
180
        ];
181
    }
182
183 1
    public function getHandlerClassName(): string
184
    {
185 1
        return CountHandler::class;
186
    }
187
}
188