Passed
Pull Request — master (#175)
by Alexander
02:36
created

Rule::formatMessage()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 9
ccs 4
cts 4
cp 1
crap 2
rs 10
c 2
b 0
f 0
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
    /**
15
     * To create an instance of a rule class, the static factory method `rule()` must be implemented
16
     * in the final class.
17
     *
18
     * Simple example:
19
     *
20
     * ```php
21
     * public static function rule(): self
22
     * {
23
     *     return new self();
24
     * }
25
     * ```
26
     *
27
     * Example with parameters:
28
     *
29
     * ```php
30
     * public static function rule(int $count, ConnectionInterface $connection): self
31
     * {
32
     *     $rule = new self();
33
     *     $rule->count = $count;
34
     *     $rule->connection = $connection;
35
     *     return $rule;
36
     * }
37
     * ```
38
     */
39
    public function __construct(
40
        private ?FormatterInterface $formatter = null,
41
        private bool $skipOnEmpty = false,
42
        private bool $skipOnError = false,
43
        /**
44
         * @var callable|null
45
         */
46
        private $when = null,
47
    ) {
48
    }
49
50
    /**
51
     * An alternative to chain calls for setting properties.
52
     *
53
     * Example:
54
     *
55
     * ```php
56
     * Rule::rule()->applyConfig(['skipOnError' => true]);
57
     * ```
58
     * is equivalent to:
59
     *
60
     * ```php
61
     * Rule::rule()->skipOnError(true);
62
     * ```
63
     *
64
     * @param array $config
65
     *
66
     * @return $this
67
     */
68 1
    final public function applyConfig($config): self
69
    {
70 1
        $rule = $this;
71 1
        foreach ($config as $methodName => $value) {
72
            $rule = $rule->$methodName($value);
73
        }
74
75 1
        return $rule;
76
    }
77
78
    /**
79
     * Validates the value
80
     *
81
     * @param mixed $value Value to be validated.
82
     * @param ValidationContext|null $context Optional validation context.
83
     *
84
     * @return Result
85
     */
86 171
    final public function validate($value, ?ValidationContext $context = null): Result
87
    {
88 171
        if ($this->skipOnEmpty && $this->isEmpty($value)) {
89 1
            return new Result();
90
        }
91
92
        if (
93 171
            ($this->skipOnError && $context && $context->getParameter(RuleSet::PARAMETER_PREVIOUS_RULES_ERRORED) === true) ||
94 171
            (is_callable($this->when) && !($this->when)($value, $context))
95
        ) {
96 2
            return new Result();
97
        }
98
99 171
        return $this->validateValue($value, $context);
100
    }
101
102
    /**
103
     * Validates the value. The method should be implemented by concrete validation rules.
104
     *
105
     * @param mixed $value Value to be validated.
106
     * @param ValidationContext|null $context Optional validation context.
107
     *
108
     * @return Result
109
     */
110
    abstract protected function validateValue($value, ?ValidationContext $context = null): Result;
111
112
    /**
113
     * @return static
114
     */
115 1
    public function withFormatter(?FormatterInterface $formatter): self
116
    {
117 1
        $new = clone $this;
118 1
        $new->formatter = $formatter;
119 1
        return $new;
120
    }
121
122 189
    protected function formatMessage(string $message, array $parameters = []): string
123
    {
124 189
        if ($this->formatter === null) {
125 189
            $this->formatter = new Formatter();
126
        }
127
128 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

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