Passed
Pull Request — master (#20134)
by Wilmer
10:17 queued 29s
created

RangeValidator   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Test Coverage

Coverage 46.51%

Importance

Changes 0
Metric Value
eloc 43
dl 0
loc 119
ccs 20
cts 43
cp 0.4651
rs 10
c 0
b 0
f 0
wmc 21

5 Methods

Rating   Name   Duplication   Size   Complexity  
A clientValidateAttribute() 0 10 2
B validateValue() 0 17 8
A init() 0 12 5
A validateAttribute() 0 6 2
A getClientOptions() 0 21 4
1
<?php
2
3
/**
4
 * @link https://www.yiiframework.com/
5
 * @copyright Copyright (c) 2008 Yii Software LLC
6
 * @license https://www.yiiframework.com/license/
7
 */
8
9
namespace yii\validators;
10
11
use Yii;
12
use yii\base\InvalidConfigException;
13
use yii\helpers\ArrayHelper;
14
use yii\helpers\Json;
15
16
/**
17
 * RangeValidator validates that the attribute value is among a list of values.
18
 *
19
 * The range can be specified via the [[range]] property.
20
 * If the [[not]] property is set true, the validator will ensure the attribute value
21
 * is NOT among the specified range.
22
 *
23
 * @author Qiang Xue <[email protected]>
24
 * @since 2.0
25
 */
26
class RangeValidator extends Validator
27
{
28
    /**
29
     * @var array|\Traversable|\Closure a list of valid values that the attribute value should be among or an anonymous function that returns
30
     * such a list. The signature of the anonymous function should be as follows,
31
     *
32
     * ```php
33
     * function($model, $attribute) {
34
     *     // compute range
35
     *     return $range;
36
     * }
37
     * ```
38
     */
39
    public $range;
40
    /**
41
     * @var bool whether the comparison is strict (both type and value must be the same)
42
     */
43
    public $strict = false;
44
    /**
45
     * @var bool whether to invert the validation logic. Defaults to false. If set to true,
46
     * the attribute value should NOT be among the list of values defined via [[range]].
47
     */
48
    public $not = false;
49
    /**
50
     * @var bool whether to allow array type attribute.
51
     */
52
    public $allowArray = false;
53
54
55
    /**
56
     * {@inheritdoc}
57
     */
58 10
    public function init()
59
    {
60 10
        parent::init();
61
        if (
62 10
            !is_array($this->range)
63 10
            && !($this->range instanceof \Closure)
64 10
            && !($this->range instanceof \Traversable)
0 ignored issues
show
introduced by
$this->range is always a sub-type of Traversable.
Loading history...
65
        ) {
66 1
            throw new InvalidConfigException('The "range" property must be set.');
67
        }
68 9
        if ($this->message === null) {
69 9
            $this->message = Yii::t('yii', '{attribute} is invalid.');
70
        }
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 8
    protected function validateValue($value)
77
    {
78 8
        $in = false;
79
80
        if (
81 8
            $this->allowArray
82 8
            && ($value instanceof \Traversable || is_array($value))
83 8
            && ArrayHelper::isSubset($value, $this->range, $this->strict)
0 ignored issues
show
Bug introduced by
It seems like $this->range can also be of type Closure; however, parameter $haystack of yii\helpers\BaseArrayHelper::isSubset() does only seem to accept iterable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

83
            && ArrayHelper::isSubset($value, /** @scrutinizer ignore-type */ $this->range, $this->strict)
Loading history...
84
        ) {
85 3
            $in = true;
86
        }
87
88 8
        if (!$in && ArrayHelper::isIn($value, $this->range, $this->strict)) {
0 ignored issues
show
Bug introduced by
It seems like $this->range can also be of type Closure; however, parameter $haystack of yii\helpers\BaseArrayHelper::isIn() does only seem to accept iterable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

88
        if (!$in && ArrayHelper::isIn($value, /** @scrutinizer ignore-type */ $this->range, $this->strict)) {
Loading history...
89 5
            $in = true;
90
        }
91
92 8
        return $this->not !== $in ? null : [$this->message, []];
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98 1
    public function validateAttribute($model, $attribute)
99
    {
100 1
        if ($this->range instanceof \Closure) {
101
            $this->range = call_user_func($this->range, $model, $attribute);
102
        }
103 1
        parent::validateAttribute($model, $attribute);
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function clientValidateAttribute($model, $attribute, $view)
110
    {
111
        if ($this->range instanceof \Closure) {
112
            $this->range = call_user_func($this->range, $model, $attribute);
113
        }
114
115
        ValidationAsset::register($view);
116
        $options = $this->getClientOptions($model, $attribute);
117
118
        return 'yii.validation.range(value, messages, ' . Json::htmlEncode($options) . ');';
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function getClientOptions($model, $attribute)
125
    {
126
        $range = [];
127
        foreach ($this->range as $value) {
128
            $range[] = (string) $value;
129
        }
130
        $options = [
131
            'range' => $range,
132
            'not' => $this->not,
133
            'message' => $this->formatMessage($this->message, [
134
                'attribute' => $model->getAttributeLabel($attribute),
135
            ]),
136
        ];
137
        if ($this->skipOnEmpty) {
138
            $options['skipOnEmpty'] = 1;
139
        }
140
        if ($this->allowArray) {
141
            $options['allowArray'] = 1;
142
        }
143
144
        return $options;
145
    }
146
}
147