Completed
Push — master ( 390789...d7ffda )
by Alexander
41:10 queued 37:45
created

NumberValidator::isNotNumber()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 8.8333
c 0
b 0
f 0
cc 7
nc 16
nop 1
crap 7
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\validators;
9
10
use Yii;
11
use yii\helpers\Json;
12
use yii\helpers\StringHelper;
13
use yii\web\JsExpression;
14
15
/**
16
 * NumberValidator validates that the attribute value is a number.
17
 *
18
 * The format of the number must match the regular expression specified in [[integerPattern]] or [[numberPattern]].
19
 * Optionally, you may configure the [[max]] and [[min]] properties to ensure the number
20
 * is within certain range.
21
 *
22
 * @author Qiang Xue <[email protected]>
23
 * @since 2.0
24
 */
25
class NumberValidator extends Validator
26
{
27
    /**
28
     * @var bool whether the attribute value can only be an integer. Defaults to false.
29
     */
30
    public $integerOnly = false;
31
    /**
32
     * @var int|float upper limit of the number. Defaults to null, meaning no upper limit.
33
     * @see tooBig for the customized message used when the number is too big.
34
     */
35
    public $max;
36
    /**
37
     * @var int|float lower limit of the number. Defaults to null, meaning no lower limit.
38
     * @see tooSmall for the customized message used when the number is too small.
39
     */
40
    public $min;
41
    /**
42
     * @var string user-defined error message used when the value is bigger than [[max]].
43
     */
44
    public $tooBig;
45
    /**
46
     * @var string user-defined error message used when the value is smaller than [[min]].
47
     */
48
    public $tooSmall;
49
    /**
50
     * @var string the regular expression for matching integers.
51
     */
52
    public $integerPattern = '/^\s*[+-]?\d+\s*$/';
53
    /**
54
     * @var string the regular expression for matching numbers. It defaults to a pattern
55
     * that matches floating numbers with optional exponential part (e.g. -1.23e-10).
56
     */
57
    public $numberPattern = '/^\s*[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\s*$/';
58
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 65
    public function init()
64
    {
65 65
        parent::init();
66 65
        if ($this->message === null) {
67 64
            $this->message = $this->integerOnly ? Yii::t('yii', '{attribute} must be an integer.')
68 56
                : Yii::t('yii', '{attribute} must be a number.');
69
        }
70 65
        if ($this->min !== null && $this->tooSmall === null) {
71 37
            $this->tooSmall = Yii::t('yii', '{attribute} must be no less than {min}.');
72
        }
73 65
        if ($this->max !== null && $this->tooBig === null) {
74 37
            $this->tooBig = Yii::t('yii', '{attribute} must be no greater than {max}.');
75
        }
76 65
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81 20
    public function validateAttribute($model, $attribute)
82
    {
83 20
        $value = $model->$attribute;
84 20
        if ($this->isNotNumber($value)) {
85 3
            $this->addError($model, $attribute, $this->message);
86 3
            return;
87
        }
88 19
        $pattern = $this->integerOnly ? $this->integerPattern : $this->numberPattern;
89
90 19
        if (!preg_match($pattern, StringHelper::normalizeNumber($value))) {
91 5
            $this->addError($model, $attribute, $this->message);
92
        }
93 19
        if ($this->min !== null && $value < $this->min) {
94 2
            $this->addError($model, $attribute, $this->tooSmall, ['min' => $this->min]);
95
        }
96 19
        if ($this->max !== null && $value > $this->max) {
97 1
            $this->addError($model, $attribute, $this->tooBig, ['max' => $this->max]);
98
        }
99 19
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104 15
    protected function validateValue($value)
105
    {
106 15
        if ($this->isNotNumber($value)) {
107 3
            return [Yii::t('yii', '{attribute} is invalid.'), []];
108
        }
109 13
        $pattern = $this->integerOnly ? $this->integerPattern : $this->numberPattern;
110 13
        if (!preg_match($pattern, StringHelper::normalizeNumber($value))) {
111 7
            return [$this->message, []];
112 12
        } elseif ($this->min !== null && $value < $this->min) {
113 2
            return [$this->tooSmall, ['min' => $this->min]];
114 12
        } elseif ($this->max !== null && $value > $this->max) {
115 2
            return [$this->tooBig, ['max' => $this->max]];
116
        }
117
118 12
        return null;
119
    }
120
121
    /*
122
     * @param mixed $value the data value to be checked.
123
     */
124 31
    private function isNotNumber($value)
125
    {
126 31
        return is_array($value)
127 31
            || is_bool($value)
128 31
            || (is_object($value) && !method_exists($value, '__toString'))
129 31
            || (!is_object($value) && !is_scalar($value) && $value !== null);
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135 1
    public function clientValidateAttribute($model, $attribute, $view)
136
    {
137 1
        ValidationAsset::register($view);
138 1
        $options = $this->getClientOptions($model, $attribute);
139
140 1
        return 'yii.validation.number(value, messages, ' . Json::htmlEncode($options) . ');';
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146 1
    public function getClientOptions($model, $attribute)
147
    {
148 1
        $label = $model->getAttributeLabel($attribute);
149
150
        $options = [
151 1
            'pattern' => new JsExpression($this->integerOnly ? $this->integerPattern : $this->numberPattern),
152 1
            'message' => $this->formatMessage($this->message, [
153 1
                'attribute' => $label,
154
            ]),
155
        ];
156
157 1
        if ($this->min !== null) {
158
            // ensure numeric value to make javascript comparison equal to PHP comparison
159
            // https://github.com/yiisoft/yii2/issues/3118
160 1
            $options['min'] = is_string($this->min) ? (float) $this->min : $this->min;
161 1
            $options['tooSmall'] = $this->formatMessage($this->tooSmall, [
162 1
                'attribute' => $label,
163 1
                'min' => $this->min,
164
            ]);
165
        }
166 1
        if ($this->max !== null) {
167
            // ensure numeric value to make javascript comparison equal to PHP comparison
168
            // https://github.com/yiisoft/yii2/issues/3118
169 1
            $options['max'] = is_string($this->max) ? (float) $this->max : $this->max;
170 1
            $options['tooBig'] = $this->formatMessage($this->tooBig, [
171 1
                'attribute' => $label,
172 1
                'max' => $this->max,
173
            ]);
174
        }
175 1
        if ($this->skipOnEmpty) {
176 1
            $options['skipOnEmpty'] = 1;
177
        }
178
179 1
        return $options;
180
    }
181
}
182