Passed
Pull Request — master (#219)
by
unknown
02:29
created

Count::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 47
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 11
dl 0
loc 47
ccs 3
cts 3
cp 1
crap 1
rs 10
c 1
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use Attribute;
8
use Countable;
9
use InvalidArgumentException;
10
use Yiisoft\Validator\FormatterInterface;
11
use Yiisoft\Validator\Result;
12
use Yiisoft\Validator\Rule;
13
use Yiisoft\Validator\ValidationContext;
14
15
use function count;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Yiisoft\Validator\Rule\count. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
16
17
/**
18
 * Validates that the value contains certain number of items. Can be applied to arrays or classes implementing
19
 * {@see Countable} interface.
20
 */
21
#[Attribute(Attribute::TARGET_PROPERTY)]
22
final class Count extends Rule
23
{
24 27
    public function __construct(
25
        /**
26
         * @var int|null minimum number of items. null means no minimum number limit.
27
         *
28
         * @see $tooFewItemsMessage for the customized message for a value with too few items.
29
         */
30
        private ?int $min = null,
31
        /**
32
         * @var int|null maximum number of items. null means no maximum number limit.
33
         *
34
         * @see $tooManyItemsMessage for the customized message for a value wuth too many items.
35
         */
36
        private ?int $max = null,
37
        /**
38
         * @var int|null exact number of items. null means no strict comparison. Mutually exclusive with {@see $min} and
39
         * {@see $max}.
40
         */
41
        private ?int $exactly = null,
42
        /**
43
         * @var string user-defined error message used when the value is neither an array nor implementing
44
         * {@see \Countable} interface.
45
         *
46
         * @see Countable
47
         */
48
        private string $message = 'This value must be an array or implement \Countable interface.',
49
        /**
50
         * @var string user-defined error message used when the number of items is smaller than {@see $min}.
51
         */
52
        private string $tooFewItemsMessage = 'This value must contain at least {min, number} ' .
53
        '{min, plural, one{item} other{items}}.',
54
        /**
55
         * @var string user-defined error message used when the number of items is greater than {@see $max}.
56
         */
57
        private string $tooManyItemsMessage = 'This value must contain at most {max, number} ' .
58
        '{max, plural, one{item} other{items}}.',
59
        /**
60
         * @var string user-defined error message used when the number of items does not equal {@see $exactly}.
61
         */
62
        private string $notExactlyMessage = 'This value must contain exactly {max, number} ' .
63
        '{max, plural, one{item} other{items}}.',
64
        ?FormatterInterface $formatter = null,
65
        bool $skipOnEmpty = false,
66
        bool $skipOnError = false,
67
        $when = null
68
    ) {
69 27
        $this->checkLimitsCompatibility();
70 22
        parent::__construct(formatter: $formatter, skipOnEmpty: $skipOnEmpty, skipOnError: $skipOnError, when: $when);
71
    }
72
73 35
    private function checkLimitsCompatibility(): void
74
    {
75 35
        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 2
            throw new InvalidArgumentException(
77
                'At least one of these attributes must be specified: $min, $max, $exactly.'
78
            );
79
        }
80
81 34
        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 6
            throw new InvalidArgumentException('$exactly is mutually exclusive with $min and $max.');
83
        }
84
85 31
        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 2
            throw new InvalidArgumentException('Use $exactly instead.');
87
        }
88
    }
89
90
    /**
91
     * @see $min
92
     */
93 5
    public function min(?int $value): self
94
    {
95 5
        $new = clone $this;
96 5
        $new->min = $value;
97
98 5
        return $new;
99
    }
100
101
    /**
102
     * @see $max
103
     */
104 4
    public function max(?int $value): self
105
    {
106 4
        $new = clone $this;
107 4
        $new->max = $value;
108
109 4
        return $new;
110
    }
111
112
    /**
113
     * @see $exactly
114
     */
115 4
    public function exactly(?int $value): self
116
    {
117 4
        $new = clone $this;
118 4
        $new->exactly = $value;
119
120 4
        return $new;
121
    }
122
123 24
    protected function validateValue($value, ?ValidationContext $context = null): Result
124
    {
125 24
        $this->checkLimitsCompatibility();
126
127 19
        $result = new Result();
128
129 19
        if (!is_countable($value)) {
130 6
            $result->addError($this->formatMessage($this->message));
131
132 6
            return $result;
133
        }
134
135 13
        $count = count($value);
136
137 13
        if ($this->exactly !== null && $count !== $this->exactly) {
138 1
            $message = $this->formatMessage($this->notExactlyMessage, ['exactly' => $this->exactly]);
139 1
            $result->addError($message);
140
141 1
            return $result;
142
        }
143
144 12
        if ($this->min !== null && $count < $this->min) {
145 3
            $message = $this->formatMessage($this->tooFewItemsMessage, ['min' => $this->min]);
146 3
            $result->addError($message);
147
        }
148
149 12
        if ($this->max !== null && $count > $this->max) {
150 2
            $message = $this->formatMessage($this->tooManyItemsMessage, ['max' => $this->max]);
151 2
            $result->addError($message);
152
        }
153
154 12
        return $result;
155
    }
156
157 4
    public function getOptions(): array
158
    {
159 4
        return array_merge(parent::getOptions(), [
160 4
            'min' => $this->min,
161 4
            'max' => $this->max,
162 4
            'exactly' => $this->exactly,
163 4
            'message' => $this->formatMessage($this->message),
164 4
            'tooFewItemsMessage' => $this->formatMessage($this->tooFewItemsMessage, ['min' => $this->min]),
165 4
            'tooManyItemsMessage' => $this->formatMessage($this->tooManyItemsMessage, ['max' => $this->max]),
166 4
            'notExactlyMessage' => $this->formatMessage($this->notExactlyMessage, ['exactly' => $this->exactly]),
167
        ]);
168
    }
169
}
170