Passed
Push — master ( 99ab46...a20a6f )
by Alexander
02:59
created

Number::rule()   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
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use Yiisoft\Strings\NumericHelper;
8
use Yiisoft\Validator\Result;
9
use Yiisoft\Validator\Rule;
10
use Yiisoft\Validator\ValidationContext;
11
12
/**
13
 * NumberValidator validates that the attribute value is a number.
14
 *
15
 * The format of the number must match the regular expression specified in {@see Number::$integerPattern}
16
 * or {@see Number::$numberPattern}. Optionally, you may configure the {@see Number::max()} and {@see Number::min()}
17
 * to ensure the number is within certain range.
18
 */
19
class Number extends Rule
20
{
21
    /**
22
     * @var bool whether the attribute value can only be an integer. Defaults to false.
23
     */
24
    private bool $asInteger = false;
25
    /**
26
     * @var float|int upper limit of the number. Defaults to null, meaning no upper limit.
27
     *
28
     * @see tooBigMessage for the customized message used when the number is too big.
29
     */
30
    private $max;
31
    /**
32
     * @var float|int lower limit of the number. Defaults to null, meaning no lower limit.
33
     *
34
     * @see tooSmallMessage for the customized message used when the number is too small.
35
     */
36
    private $min;
37
    /**
38
     * @var string user-defined error message used when the value is bigger than {@link $max}.
39
     */
40
    private string $tooBigMessage = 'Value must be no greater than {max}.';
41
    /**
42
     * @var string user-defined error message used when the value is smaller than {@link $min}.
43
     */
44
    private string $tooSmallMessage = 'Value must be no less than {min}.';
45
    /**
46
     * @var string the regular expression for matching integers.
47
     */
48
    private string $integerPattern = '/^\s*[+-]?\d+\s*$/';
49
    /**
50
     * @var string the regular expression for matching numbers. It defaults to a pattern
51
     * that matches floating numbers with optional exponential part (e.g. -1.23e-10).
52
     */
53
    private string $numberPattern = '/^\s*[-+]?\d*\.?\d+([eE][-+]?\d+)?\s*$/';
54
55 35
    public static function rule(): self
56
    {
57 35
        return new self();
58
    }
59
60 36
    protected function validateValue($value, ValidationContext $context = null): Result
61
    {
62 36
        $result = new Result();
63
64 36
        if (is_bool($value) || !is_scalar($value)) {
65 5
            $result->addError($this->formatMessage($this->getNotANumberMessage(), ['value' => $value]));
66 5
            return $result;
67
        }
68
69 31
        $pattern = $this->asInteger ? $this->integerPattern : $this->numberPattern;
70
71 31
        if (!preg_match($pattern, NumericHelper::normalize($value))) {
72 9
            $result->addError($this->formatMessage($this->getNotANumberMessage(), ['value' => $value]));
73 28
        } elseif ($this->min !== null && $value < $this->min) {
74 12
            $result->addError($this->formatMessage($this->tooSmallMessage, ['min' => $this->min]));
75 23
        } elseif ($this->max !== null && $value > $this->max) {
76 7
            $result->addError($this->formatMessage($this->tooBigMessage, ['max' => $this->max]));
77
        }
78
79 31
        return $result;
80
    }
81
82 9
    public function integer(): self
83
    {
84 9
        $new = clone $this;
85 9
        $new->asInteger = true;
86 9
        return $new;
87
    }
88
89 13
    public function min($value): self
90
    {
91 13
        $new = clone $this;
92 13
        $new->min = $value;
93 13
        return $new;
94
    }
95
96 14
    public function max($value): self
97
    {
98 14
        $new = clone $this;
99 14
        $new->max = $value;
100 14
        return $new;
101
    }
102
103 1
    public function tooSmallMessage(string $message): self
104
    {
105 1
        $new = clone $this;
106 1
        $new->tooSmallMessage = $message;
107 1
        return $new;
108
    }
109
110
    public function tooBigMessage(string $message): self
111
    {
112
        $new = clone $this;
113
        $new->tooBigMessage = $message;
114
        return $new;
115
    }
116
117 22
    private function getNotANumberMessage(): string
118
    {
119 22
        if ($this->asInteger === true) {
120 9
            return 'Value must be an integer.';
121
        }
122 14
        return 'Value must be a number.';
123
    }
124
125 8
    public function getOptions(): array
126
    {
127 8
        return array_merge(
128 8
            parent::getOptions(),
129
            [
130 8
                'notANumberMessage' => $this->formatMessage($this->getNotANumberMessage()),
131 8
                'asInteger' => $this->asInteger,
132 8
                'min' => $this->min,
133 8
                'tooSmallMessage' => $this->formatMessage($this->tooSmallMessage, ['min' => $this->min]),
134 8
                'max' => $this->max,
135 8
                'tooBigMessage' => $this->formatMessage($this->tooBigMessage, ['max' => $this->max]),
136
            ],
137
        );
138
    }
139
}
140