Passed
Pull Request — master (#559)
by Sergei
02:45
created

ValidationContext   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 289
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 50
c 2
b 0
f 0
dl 0
loc 289
ccs 18
cts 18
cp 1
rs 10
wmc 22

15 Methods

Rating   Name   Duplication   Size   Complexity  
A setContextDataOnce() 0 16 2
A getGlobalDataSet() 0 4 1
A getParameter() 0 3 1
A getRawData() 0 4 1
A setAttributeTranslator() 0 4 1
A getAttribute() 0 3 1
A validate() 0 13 1
A isAttributeMissing() 0 3 2
A getTranslatedAttribute() 0 15 4
A __construct() 0 4 1
A setDataSet() 0 4 1
A setParameter() 0 4 1
A requireValidator() 0 4 2
A getDataSet() 0 7 2
A setAttribute() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator;
6
7
use RuntimeException;
8
use Yiisoft\Arrays\ArrayHelper;
9
use Yiisoft\Validator\Rule\Nested;
10
use Yiisoft\Validator\Rule\StopOnError;
11
12
/**
13
 * Validation context that might be taken into account when performing validation.
14
 *
15
 * @psalm-import-type RulesType from ValidatorInterface
16
 */
17
final class ValidationContext
18
{
19 827
    public const PARAMETER_VALUE_AS_ARRAY = 'yii-validator-value-as-array';
20
21
    /**
22
     * A name of parameter indicating that previous rule in the set caused validation error. Used to allow skipping of
23
     * the current rule:
24
     *
25
     * - in {@see Validator} for rules implementing {@see SkipOnErrorInterface}.
26
     * - for {@see StopOnError} rule (no additional configuration is needed).
27 43
     */
28
    public const PARAMETER_PREVIOUS_RULES_ERRORED = 'yii-validator-previous-rules-errored';
29 43
30
    /**
31
     * @var ValidatorInterface|null A validator instance. `null` means context data was not set
32
     * with {@see setContextDataOnce()} yet.
33
     */
34
    private ?ValidatorInterface $validator = null;
35 11
36
    /**
37 11
     * @var mixed The raw validated data. `null` means context data was not set with {@see setContextDataOnce()} yet.
38
     */
39
    private mixed $rawData = null;
40
41
    /**
42
     * @var DataSetInterface|null Global data set. `null` if data set was not set with {@see setContextDataOnce()} yet.
43 503
     */
44
    private ?DataSetInterface $globalDataSet = null;
45 503
46
    /**
47
     * @var DataSetInterface|null Current scope's data set the attribute belongs to. `null` if data set was not set
48
     * with {@see setDataSet()} yet.
49
     */
50
    private ?DataSetInterface $dataSet = null;
51 140
52
    /**
53 140
     * @var string|null Validated data set's attribute name. `null` if a single value is validated.
54 140
     */
55
    private ?string $attribute = null;
56
57
    /**
58
     * @var AttributeTranslatorInterface|null Default attribute translator to use if attribute translator is not set.
59
     */
60 3
    private ?AttributeTranslatorInterface $defaultAttributeTranslator = null;
61
62 3
    /**
63
     * @param array $parameters Arbitrary parameters.
64
     * @param AttributeTranslatorInterface|null $attributeTranslator Optional attribute translator instance to use.
65
     * If `null` is provided, or it's not specified, a default translator passed through
66
     * {@see setContextDataOnce()} is used.
67
     */
68
    public function __construct(
69
        private array $parameters = [],
70
        private ?AttributeTranslatorInterface $attributeTranslator = null,
71
    ) {
72
    }
73
74
    /**
75 6
     * Set context data if it is not set yet.
76
     *
77 6
     * @param ValidatorInterface $validator A validator instance.
78
     * @param AttributeTranslatorInterface $attributeTranslator Attribute translator to use by default. If translator
79
     * is specified via {@see setAttributeTranslator()}, it will be used instead.
80 503
     * @param mixed $rawData The raw validated data.
81
     * @param DataSetInterface $dataSet Global data set ({@see $globalDataSet}).
82 503
     *
83
     * @internal
84
     *
85 786
     * @return $this The same instance of validation context.
86
     */
87 786
    public function setContextDataOnce(
88
        ValidatorInterface $validator,
89
        AttributeTranslatorInterface $attributeTranslator,
90
        mixed $rawData,
91
        DataSetInterface $dataSet,
92
    ): self {
93
        if ($this->validator !== null) {
94
            return $this;
95
        }
96
97
        $this->validator = $validator;
98
        $this->defaultAttributeTranslator = $attributeTranslator;
99
        $this->rawData = $rawData;
100
        $this->globalDataSet = $dataSet;
101
102
        return $this;
103
    }
104
105
    /**
106
     * Set attribute translator to use.
107
     *
108
     * @param AttributeTranslatorInterface|null $attributeTranslator Attribute translator to use. If `null`,
109
     * translator passed in {@see setContextData()} will be used.
110
     *
111
     * @return $this The same instance of validation context.
112
     */
113
    public function setAttributeTranslator(?AttributeTranslatorInterface $attributeTranslator): self
114
    {
115
        $this->attributeTranslator = $attributeTranslator;
116
        return $this;
117
    }
118
119
    /**
120
     * Validate data in current context.
121
     *
122
     * @param mixed $data Data set to validate. If {@see RulesProviderInterface} instance provided and rules are
123
     * not specified explicitly, they are read from the {@see RulesProviderInterface::getRules()}.
124
     * @param callable|iterable|object|string|null $rules Rules to apply. If specified, rules are not read from data set
125
     * even if it is an instance of {@see RulesProviderInterface}.
126
     *
127
     * @psalm-param RulesType $rules
128
     *
129
     * @throws RuntimeException If validator is not set in validation context.
130
     *
131
     * @return Result Validation result.
132
     */
133
    public function validate(mixed $data, callable|iterable|object|string|null $rules = null): Result
134
    {
135
        $this->requireValidator();
136
137
        $currentDataSet = $this->dataSet;
138
        $currentAttribute = $this->attribute;
139
140
        $result = $this->validator->validate($data, $rules, $this);
0 ignored issues
show
Bug introduced by
The method validate() 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

140
        /** @scrutinizer ignore-call */ 
141
        $result = $this->validator->validate($data, $rules, $this);

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...
141
142
        $this->dataSet = $currentDataSet;
143
        $this->attribute = $currentAttribute;
144
145
        return $result;
146
    }
147
148
    /**
149
     * Get the raw validated data.
150
     *
151
     * @throws RuntimeException If validator is not set in validation context.
152
     *
153
     * @return mixed The raw validated data.
154
     */
155
    public function getRawData(): mixed
156
    {
157
        $this->requireValidator();
158
        return $this->rawData;
159
    }
160
161
    /**
162
     * Get the global data set.
163
     *
164
     * @return DataSetInterface Data set instance.
165
     *
166
     * @see $globalDataSet
167
     */
168
    public function getGlobalDataSet(): DataSetInterface
169
    {
170
        $this->requireValidator();
171
        return $this->globalDataSet;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->globalDataSet could return the type null which is incompatible with the type-hinted return Yiisoft\Validator\DataSetInterface. Consider adding an additional type-check to rule them out.
Loading history...
172
    }
173
174
    /**
175
     * Get the current scope's data set the attribute belongs to.
176
     *
177
     * @return DataSetInterface Data set instance.
178
     *
179
     * @see $dataSet
180
     */
181
    public function getDataSet(): DataSetInterface
182
    {
183
        if ($this->dataSet === null) {
184
            throw new RuntimeException('Data set in validation context is not set.');
185
        }
186
187
        return $this->dataSet;
188
    }
189
190
    /**
191
     * Set the current scope's data set the attribute belongs to.
192
     *
193
     * @param DataSetInterface $dataSet Data set instance.
194
     *
195
     * @return $this The same instance of validation context.
196
     *
197
     * @internal
198
     *
199
     * @see $dataSet
200
     */
201
    public function setDataSet(DataSetInterface $dataSet): self
202
    {
203
        $this->dataSet = $dataSet;
204
        return $this;
205
    }
206
207
    /**
208
     * Get validated data set's attribute name.
209
     *
210
     * @return string|null Validated data set's attribute name. `null` if a single value is validated.
211
     */
212
    public function getAttribute(): ?string
213
    {
214
        return $this->attribute;
215
    }
216
217
    /**
218
     * Get translated attribute name.
219
     *
220
     * @return string|null Translated attribute name. `null` if a single value is validated and there is nothing
221
     * to translate.
222
     */
223
    public function getTranslatedAttribute(): ?string
224
    {
225
        if ($this->attribute === null) {
226
            return null;
227
        }
228
229
        if ($this->attributeTranslator !== null) {
230
            return $this->attributeTranslator->translate($this->attribute);
231
        }
232
233
        if ($this->defaultAttributeTranslator !== null) {
234
            return $this->defaultAttributeTranslator->translate($this->attribute);
235
        }
236
237
        return $this->attribute;
238
    }
239
240
    /**
241
     * Set the name of the attribute validated.
242
     *
243
     * @param string|null $attribute Validated attribute name. Null if a single value is validated.
244
     *
245
     * @return $this The same instance of validation context.
246
     *
247
     * @internal
248
     */
249
    public function setAttribute(?string $attribute): self
250
    {
251
        $this->attribute = $attribute;
252
        return $this;
253
    }
254
255
    /**
256
     * Get named parameter.
257
     *
258
     * @param string $name Parameter name.
259
     * @param mixed $default Default value to return in case parameter with a given name does not exist.
260
     *
261
     * @return mixed Parameter value.
262
     *
263
     * @see ArrayHelper::getValue()
264
     */
265
    public function getParameter(string $name, mixed $default = null): mixed
266
    {
267
        return ArrayHelper::getValue($this->parameters, $name, $default);
268
    }
269
270
    /**
271
     * Set parameter value.
272
     *
273
     * @param string $name Parameter name.
274
     * @param mixed $value Parameter value.
275
     *
276
     * @return $this The same instance of validation context.
277
     */
278
    public function setParameter(string $name, mixed $value): self
279
    {
280
        $this->parameters[$name] = $value;
281
        return $this;
282
    }
283
284
    /**
285
     * Check whether {@see $attribute} is missing in a {@see $dataSet}.
286
     *
287
     * @return bool Whether {@see $attribute} is missing in a {@see $dataSet}.
288
     */
289
    public function isAttributeMissing(): bool
290
    {
291
        return $this->attribute !== null && !$this->getDataSet()->hasAttribute($this->attribute);
292
    }
293
294
    /**
295
     * Ensure that validator is set in validation context.
296
     *
297
     * @psalm-assert ValidatorInterface $this->validator
298
     * @psalm-assert DataSetInterface $this->globalDataSet
299
     *
300
     * @throws RuntimeException If validator is not set in validation context.
301
     */
302
    private function requireValidator(): void
303
    {
304
        if ($this->validator === null) {
305
            throw new RuntimeException('Validator is not set in validation context.');
306
        }
307
    }
308
}
309