Passed
Pull Request — master (#175)
by
unknown
02:55
created

Rule   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 215
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 38
dl 0
loc 215
ccs 43
cts 43
cp 1
rs 10
c 5
b 0
f 0
wmc 22

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
B validate() 0 14 8
A getOptions() 0 5 1
A when() 0 5 1
A isEmpty() 0 3 3
A applyConfig() 0 8 2
A skipOnError() 0 5 1
A withFormatter() 0 5 1
A formatMessage() 0 9 2
A skipOnEmpty() 0 5 1
A getName() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator;
6
7
use function is_callable;
8
9
/**
10
 * Rule represents a single value validation rule.
11
 */
12
abstract class Rule implements RuleInterface, ParametrizedRuleInterface, FormattableRuleInterface
13
{
14
    private ?FormatterInterface $formatter = null;
15
    private bool $skipOnEmpty = false;
16
    private bool $skipOnError = false;
17
18
    /**
19
     * @var callable|null
20
     */
21
    private $when = null;
22
23
    /**
24
     * To create an instance of a rule class, the static factory method `rule()` must be implemented
25
     * in the final class.
26
     *
27
     * Simple example:
28
     *
29
     * ```php
30
     * public static function rule(): self
31
     * {
32
     *     return new self();
33
     * }
34
     * ```
35
     *
36
     * Example with parameters:
37
     *
38
     * ```php
39
     * public static function rule(int $count, ConnectionInterface $connection): self
40
     * {
41
     *     $rule = new self();
42
     *     $rule->count = $count;
43
     *     $rule->connection = $connection;
44
     *     return $rule;
45
     * }
46
     * ```
47
     */
48 184
    final protected function __construct()
49
    {
50 184
    }
51
52
    /**
53
     * An alternative to chain calls for setting properties.
54
     *
55
     * Example:
56
     *
57
     * ```php
58
     * Rule::rule()->applyConfig(['skipOnError' => true]);
59
     * ```
60
     * is equivalent to:
61
     *
62
     * ```php
63
     * Rule::rule()->skipOnError(true);
64
     * ```
65
     *
66
     * @param array $config
67
     *
68
     * @return $this
69
     */
70 1
    final public function applyConfig($config): self
71
    {
72 1
        $rule = $this;
73 1
        foreach ($config as $methodName => $value) {
74 1
            $rule = $rule->$methodName($value);
75
        }
76
77 1
        return $rule;
78
    }
79
80
    /**
81
     * Validates the value
82
     *
83
     * @param mixed $value Value to be validated.
84
     * @param ValidationContext|null $context Optional validation context.
85
     *
86
     * @return Result
87
     */
88 171
    final public function validate($value, ?ValidationContext $context = null): Result
89
    {
90 171
        if ($this->skipOnEmpty && $this->isEmpty($value)) {
91 1
            return new Result();
92
        }
93
94
        if (
95 171
            ($this->skipOnError && $context && $context->getParameter(RuleSet::PARAMETER_PREVIOUS_RULES_ERRORED) === true) ||
96 171
            (is_callable($this->when) && !($this->when)($value, $context))
97
        ) {
98 2
            return new Result();
99
        }
100
101 171
        return $this->validateValue($value, $context);
102
    }
103
104
    /**
105
     * Validates the value. The method should be implemented by concrete validation rules.
106
     *
107
     * @param mixed $value Value to be validated.
108
     * @param ValidationContext|null $context Optional validation context.
109
     *
110
     * @return Result
111
     */
112
    abstract protected function validateValue($value, ?ValidationContext $context = null): Result;
113
114
    /**
115
     * @return static
116
     */
117 1
    public function withFormatter(?FormatterInterface $formatter): self
118
    {
119 1
        $new = clone $this;
120 1
        $new->formatter = $formatter;
121 1
        return $new;
122
    }
123
124 189
    protected function formatMessage(string $message, array $parameters = []): string
125
    {
126 189
        if ($this->formatter === null) {
127 189
            $this->formatter = new Formatter();
128
        }
129
130 189
        return $this->formatter->format(
0 ignored issues
show
Bug introduced by
The method format() does not exist on null. ( Ignorable by Annotation )

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

130
        return $this->formatter->/** @scrutinizer ignore-call */ format(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
131 189
            $message,
132
            $parameters
133
        );
134
    }
135
136
    /**
137
     * Add a PHP callable whose return value determines whether this rule should be applied.
138
     * By default, rule will always be applied.
139
     *
140
     * The signature of the callable should be `function ($value, ValidationContext $context): bool`,
141
     * where `$value` and `$context` refer to the value validated and the validation context.
142
     * The callable should return a boolean value.
143
     *
144
     * The following example will enable the validator only when the country currently selected is USA:
145
     *
146
     * ```php
147
     * function ($value, ValidationContext $context)) {
148
     *     if ($context === null) {
149
     *         return false;
150
     *     }
151
     *
152
     *     $dataSet = $context->getDataSet();
153
     *     if ($dataSet === null) {
154
     *         return false;
155
     *     }
156
     *
157
     *     return $dataSet->getAttributeValue('country') === Country::USA;
158
     * }
159
     * ```
160
     *
161
     * @param callable $callback
162
     *
163
     * @return static
164
     */
165 1
    public function when(callable $callback): self
166
    {
167 1
        $new = clone $this;
168 1
        $new->when = $callback;
169 1
        return $new;
170
    }
171
172 6
    public function skipOnError(bool $value): self
173
    {
174 6
        $new = clone $this;
175 6
        $new->skipOnError = $value;
176 6
        return $new;
177
    }
178
179
    /**
180
     * @param bool $value if validation should be skipped if value validated is empty
181
     *
182
     * @return static
183
     */
184 2
    public function skipOnEmpty(bool $value): self
185
    {
186 2
        $new = clone $this;
187 2
        $new->skipOnEmpty = $value;
188 2
        return $new;
189
    }
190
191
    /**
192
     * Checks if the given value is empty.
193
     * A value is considered empty if it is null, an empty array, or an empty string.
194
     * Note that this method is different from PHP empty(). It will return false when the value is 0.
195
     *
196
     * @param mixed $value the value to be checked
197
     *
198
     * @return bool whether the value is empty
199
     */
200 18
    protected function isEmpty($value): bool
201
    {
202 18
        return $value === null || $value === [] || $value === '';
203
    }
204
205
    /**
206
     * Get name of the rule to be used when rule is converted to array.
207
     * By default it returns base name of the class, first letter in lowercase.
208
     *
209
     * @return string
210
     */
211 22
    public function getName(): string
212
    {
213 22
        $className = static::class;
214 22
        return lcfirst(substr($className, strrpos($className, '\\') + 1));
215
    }
216
217
    /**
218
     * Returns rule options as array.
219
     *
220
     * @return array
221
     */
222 58
    public function getOptions(): array
223
    {
224
        return [
225 58
            'skipOnEmpty' => $this->skipOnEmpty,
226 58
            'skipOnError' => $this->skipOnError,
227
        ];
228
    }
229
}
230