Test Failed
Pull Request — master (#175)
by
unknown
05:56 queued 03:39
created

CompareTo::getMessage()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 23
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 10.0244

Importance

Changes 0
Metric Value
cc 10
eloc 19
c 0
b 0
f 0
nc 10
nop 0
dl 0
loc 23
ccs 15
cts 16
cp 0.9375
crap 10.0244
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use Attribute;
8
use InvalidArgumentException;
9
use RuntimeException;
10
use Yiisoft\Validator\FormatterInterface;
11
use Yiisoft\Validator\Result;
12
use Yiisoft\Validator\Rule;
13
use Yiisoft\Validator\ValidationContext;
14
15
/**
16
 * CompareValidator compares the specified attribute value with another value.
17
 *
18
 * The value being compared with a constant {@see CompareTo::$compareValue}, which is set
19
 * in the constructor.
20
 *
21
 * CompareValidator supports different comparison operators, specified
22
 * via the {@see CompareTo::$operator}.
23
 *
24
 * The default comparison function is based on string values, which means the values
25
 * are compared byte by byte. When comparing numbers, make sure to call the {@see CompareTo::$asNumber}
26
 * to enable numeric comparison.
27
 */
28
#[Attribute(Attribute::TARGET_PROPERTY)]
29
final class CompareTo extends Rule
30
{
31
    /**
32
     * Constant for specifying the comparison as string values.
33
     * No conversion will be done before comparison.
34
     *
35
     * @see $type
36
     */
37
    public const TYPE_STRING = 'string';
38
    /**
39
     * Constant for specifying the comparison as numeric values.
40
     * String values will be converted into numbers before comparison.
41
     *
42
     * @see $type
43
     */
44
    public const TYPE_NUMBER = 'number';
45
    private array $validOperators = [
46
        '==' => 1,
47
        '===' => 1,
48
        '!=' => 1,
49
        '!==' => 1,
50
        '>' => 1,
51
        '>=' => 1,
52
        '<' => 1,
53
        '<=' => 1,
54
    ];
55
56
    public function __construct(
57
        /**
58
         * @var mixed the constant value to be compared with.
59
         */
60
        private $compareValue,
61
        /**
62
         * @var string|null user-defined error message
63
         */
64
        private ?string $message = null,
65
        /**
66
         * @var string the type of the values being compared.
67
         */
68
        private string $type = self::TYPE_STRING,
69
        /**
70
         * @var string the operator for comparison. The following operators are supported:
71
         *
72
         * - `==`: check if two values are equal. The comparison is done is non-strict mode.
73
         * - `===`: check if two values are equal. The comparison is done is strict mode.
74
         * - `!=`: check if two values are NOT equal. The comparison is done is non-strict mode.
75
         * - `!==`: check if two values are NOT equal. The comparison is done is strict mode.
76
         * - `>`: check if value being validated is greater than the value being compared with.
77
         * - `>=`: check if value being validated is greater than or equal to the value being compared with.
78
         * - `<`: check if value being validated is less than the value being compared with.
79
         * - `<=`: check if value being validated is less than or equal to the value being compared with.
80
         *
81
         * When you want to compare numbers, make sure to also call {@see $asNumber}.
82
         */
83
        private string $operator = '==',
84
        ?FormatterInterface $formatter = null,
85 8
        bool $skipOnEmpty = false,
86
        bool $skipOnError = false,
87 8
        $when = null
88 1
    ) {
89
        parent::__construct(formatter: $formatter, skipOnEmpty: $skipOnEmpty, skipOnError: $skipOnError, when: $when);
90
91 7
        if (!isset($this->validOperators[$operator])) {
92 7
            throw new InvalidArgumentException("Operator \"$operator\" is not supported.");
93 3
        }
94 5
    }
95 3
96 3
    private function getMessage(): string
97 2
    {
98 2
        if ($this->message !== null) {
99 1
            return $this->message;
100 2
        }
101 2
102 1
        switch ($this->operator) {
103 1
            case '==':
104 1
            case '===':
105 1
                return 'Value must be equal to "{value}".';
106
            case '!=':
107
            case '!==':
108
                return 'Value must not be equal to "{value}".';
109
            case '>':
110
                return 'Value must be greater than "{value}".';
111 3
            case '>=':
112
                return 'Value must be greater than or equal to "{value}".';
113 3
            case '<':
114 3
                return 'Value must be less than "{value}".';
115 3
            case '<=':
116
                return 'Value must be less than or equal to "{value}".';
117
            default:
118 1
                throw new RuntimeException("Unknown operator: {$this->operator}");
119
        }
120 1
    }
121
122
    protected function validateValue($value, ?ValidationContext $context = null): Result
123
    {
124 1
        $result = new Result();
125 1
126 1
        if (!$this->compareValues($this->operator, $this->type, $value, $this->compareValue)) {
127
            $message = $this->formatMessage($this->getMessage(), ['value' => $this->compareValue]);
128
            $result->addError($message);
129
        }
130
131
        return $result;
132
    }
133
134
    /**
135
     * Compares two values with the specified operator.
136
     *
137
     * @param string $operator the comparison operator
138
     * @param string $type the type of the values being compared
139
     * @param mixed $value the value being compared
140
     * @param mixed $compareValue another value being compared
141
     *
142
     * @return bool whether the comparison using the specified operator is true.
143 2
     */
144
    protected function compareValues(string $operator, string $type, $value, $compareValue): bool
145 2
    {
146
        if ($type === self::TYPE_NUMBER) {
147 2
            $value = (float) $value;
148 1
            $compareValue = (float)$compareValue;
149 1
        } else {
150 1
            $value = (string) $value;
151
            $compareValue = (string) $compareValue;
152 1
        }
153
        switch ($operator) {
154
            case '==':
155
                return $value == $compareValue;
156
            case '===':
157
                return $value === $compareValue;
158 2
            case '!=':
159
                return $value != $compareValue;
160
            case '!==':
161
                return $value !== $compareValue;
162
            case '>':
163
                return $value > $compareValue;
164
            case '>=':
165
                return $value >= $compareValue;
166
            case '<':
167
                return $value < $compareValue;
168
            case '<=':
169
                return $value <= $compareValue;
170
            default:
171 2
                return false;
172
        }
173 2
    }
174
175
    public function getOptions(): array
176
    {
177 2
        return array_merge(parent::getOptions(), [
178 2
            'compareValue' => $this->compareValue,
179
            'message' => $this->formatMessage($this->getMessage(), ['value' => $this->compareValue]),
180 2
            'type' => $this->type,
181 2
            'operator' => $this->operator,
182 2
        ]);
183 1
    }
184
}
185