Passed
Pull Request — master (#599)
by Sergei
09:48 queued 07:08
created

ValidationContext::isInitialized()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
cc 1
rs 10
ccs 0
cts 0
cp 0
crap 2
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\StopOnError;
10
11
/**
12
 * Validation context that might be taken into account when performing validation.
13
 *
14
 * @psalm-import-type RawRules from ValidatorInterface
15
 */
16
final class ValidationContext
17
{
18
    /**
19 827
     * A name of parameter storing validated value as array. For rules working with arrays it helps to prevent extra
20
     * conversion of a validated value to array. The parameter's value type is either `array` or `null`. `null` means
21
     * the original value must be used.
22
     */
23
    public const PARAMETER_VALUE_AS_ARRAY = 'yii-validator-value-as-array';
24
25
    /**
26
     * A name of parameter indicating that previous rule in the set caused validation error. Used to allow skipping of
27 43
     * the current rule:
28
     *
29 43
     * - in {@see Validator} for rules implementing {@see SkipOnErrorInterface}.
30
     * - for {@see StopOnError} rule (no additional configuration is needed).
31
     */
32
    public const PARAMETER_PREVIOUS_RULES_ERRORED = 'yii-validator-previous-rules-errored';
33
34
    /**
35 11
     * A name of parameter that allow use predefined {@see Result}.
36
     */
37 11
    public const PARAMETER_PREDEFINED_RESULT = 'yii-validator-predefined-result';
38
39
    /**
40
     * @var ValidatorInterface|null A validator instance. `null` means context data was not set
41
     * with {@see setContextDataOnce()} yet.
42
     */
43 503
    private ?ValidatorInterface $validator = null;
44
45 503
    /**
46
     * @var mixed The raw validated data. `null` means context data was not set with {@see setContextDataOnce()} yet.
47
     */
48
    private mixed $rawData = null;
49
50
    /**
51 140
     * @var DataSetInterface|null Global data set. `null` if data set was not set with {@see setContextDataOnce()} yet.
52
     */
53 140
    private ?DataSetInterface $globalDataSet = null;
54 140
55
    /**
56
     * @var DataSetInterface|null Current scope's data set the attribute belongs to. `null` if data set was not set
57
     * with {@see setDataSet()} yet.
58
     */
59
    private ?DataSetInterface $dataSet = null;
60 3
61
    /**
62 3
     * @var string|null Validated data set's attribute name. `null` if a single value is validated.
63
     */
64
    private ?string $attribute = null;
65
66
    /**
67
     * @var AttributeTranslatorInterface|null Default attribute translator to use if attribute translator is not set.
68
     */
69
    private ?AttributeTranslatorInterface $defaultAttributeTranslator = null;
70
71
    /**
72
     * @var bool Whether {@see $dataSet} is missing.
73
     */
74
    private bool $isDataSetMissing = false;
75 6
76
    /**
77 6
     * @param array $parameters Arbitrary parameters.
78
     * @param AttributeTranslatorInterface|null $attributeTranslator Optional attribute translator instance to use.
79
     * If `null` is provided, or it's not specified, a default translator passed through
80 503
     * {@see setContextDataOnce()} is used.
81
     */
82 503
    public function __construct(
83
        private array $parameters = [],
84
        private ?AttributeTranslatorInterface $attributeTranslator = null,
85 786
    ) {
86
    }
87 786
88
    /**
89
     * Set context data if it is not set yet.
90
     *
91
     * @param ValidatorInterface $validator A validator instance.
92
     * @param AttributeTranslatorInterface $attributeTranslator Attribute translator to use by default. If translator
93
     * is specified via {@see setAttributeTranslator()}, it will be used instead.
94
     * @param mixed $rawData The raw validated data.
95
     * @param DataSetInterface $dataSet Global data set ({@see $globalDataSet}).
96
     *
97
     * @internal
98
     *
99
     * @return $this The same instance of validation context.
100
     */
101
    public function setContextDataOnce(
102
        ValidatorInterface $validator,
103
        AttributeTranslatorInterface $attributeTranslator,
104
        mixed $rawData,
105
        DataSetInterface $dataSet,
106
    ): self {
107
        if ($this->isInitialized()) {
108
            return $this;
109
        }
110
111
        $this->validator = $validator;
112
        $this->defaultAttributeTranslator = $attributeTranslator;
113
        $this->rawData = $rawData;
114
        $this->globalDataSet = $dataSet;
115
116
        return $this;
117
    }
118
119
    /**
120
     * @internal
121
     */
122
    public function isInitialized(): bool
123
    {
124
        return $this->validator !== null;
125
    }
126
127
    /**
128
     * Set attribute translator to use.
129
     *
130
     * @param AttributeTranslatorInterface|null $attributeTranslator Attribute translator to use. If `null`,
131
     * translator passed in {@see setContextData()} will be used.
132
     *
133
     * @return $this The same instance of validation context.
134
     */
135
    public function setAttributeTranslator(?AttributeTranslatorInterface $attributeTranslator): self
136
    {
137
        $this->attributeTranslator = $attributeTranslator;
138
        return $this;
139
    }
140
141
    /**
142
     * Validate data in current context.
143
     *
144
     * @param mixed $data Data set to validate. If {@see RulesProviderInterface} instance provided and rules are
145
     * not specified explicitly, they are read from the {@see RulesProviderInterface::getRules()}.
146
     * @param callable|iterable|object|string|null $rules Rules to apply. If specified, rules are not read from data set
147
     * even if it is an instance of {@see RulesProviderInterface}.
148
     *
149
     * @psalm-param RawRules|null $rules
150
     *
151
     * @throws RuntimeException If validator is not set in validation context.
152
     *
153
     * @return Result Validation result.
154
     */
155
    public function validate(mixed $data, callable|iterable|object|string|null $rules = null): Result
156
    {
157
        $this->requireValidator();
158
159
        $currentDataSet = $this->dataSet;
160
        $currentAttribute = $this->attribute;
161
        $isCurrentDataSetMissing = $this->isDataSetMissing;
162
163
        // The lack of an attribute means that in the context of further validation there is no data set at all.
164
        $this->isDataSetMissing = $this->isAttributeMissing();
165
        $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

165
        /** @scrutinizer ignore-call */ 
166
        $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...
166
167
        $this->dataSet = $currentDataSet;
168
        $this->attribute = $currentAttribute;
169
        $this->isDataSetMissing = $isCurrentDataSetMissing;
170
171
        return $result;
172
    }
173
174
    /**
175
     * Get the raw validated data.
176
     *
177
     * @throws RuntimeException If validator is not set in validation context.
178
     *
179
     * @return mixed The raw validated data.
180
     */
181
    public function getRawData(): mixed
182
    {
183
        $this->requireValidator();
184
        return $this->rawData;
185
    }
186
187
    /**
188
     * Get the global data set.
189
     *
190
     * @return DataSetInterface Data set instance.
191
     *
192
     * @see $globalDataSet
193
     */
194
    public function getGlobalDataSet(): DataSetInterface
195
    {
196
        $this->requireValidator();
197
        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...
198
    }
199
200
    /**
201
     * Get the current scope's data set the attribute belongs to.
202
     *
203
     * @return DataSetInterface Data set instance.
204
     *
205
     * @see $dataSet
206
     */
207
    public function getDataSet(): DataSetInterface
208
    {
209
        if ($this->dataSet === null) {
210
            throw new RuntimeException('Data set in validation context is not set.');
211
        }
212
213
        return $this->dataSet;
214
    }
215
216
    /**
217
     * Set the current scope's data set the attribute belongs to.
218
     *
219
     * @param DataSetInterface $dataSet Data set instance.
220
     *
221
     * @return $this The same instance of validation context.
222
     *
223
     * @internal
224
     *
225
     * @see $dataSet
226
     */
227
    public function setDataSet(DataSetInterface $dataSet): self
228
    {
229
        $this->dataSet = $dataSet;
230
        return $this;
231
    }
232
233
    /**
234
     * Get validated data set's attribute name.
235
     *
236
     * @return string|null Validated data set's attribute name. `null` if a single value is validated.
237
     */
238
    public function getAttribute(): ?string
239
    {
240
        return $this->attribute;
241
    }
242
243
    /**
244
     * Get translated attribute name.
245
     *
246
     * @return string|null Translated attribute name. `null` if a single value is validated and there is nothing
247
     * to translate.
248
     */
249
    public function getTranslatedAttribute(): ?string
250
    {
251
        if ($this->attribute === null) {
252
            return null;
253
        }
254
255
        if ($this->attributeTranslator !== null) {
256
            return $this->attributeTranslator->translate($this->attribute);
257
        }
258
259
        if ($this->defaultAttributeTranslator !== null) {
260
            return $this->defaultAttributeTranslator->translate($this->attribute);
261
        }
262
263
        return $this->attribute;
264
    }
265
266
    /**
267
     * Set the name of the attribute validated.
268
     *
269
     * @param string|null $attribute Validated attribute name. Null if a single value is validated.
270
     *
271
     * @return $this The same instance of validation context.
272
     *
273
     * @internal
274
     */
275
    public function setAttribute(?string $attribute): self
276
    {
277
        $this->attribute = $attribute;
278
        return $this;
279
    }
280
281
    /**
282
     * Get named parameter.
283
     *
284
     * @param string $name Parameter name.
285
     * @param mixed $default Default value to return in case parameter with a given name does not exist.
286
     *
287
     * @return mixed Parameter value.
288
     *
289
     * @see ArrayHelper::getValue()
290
     */
291
    public function getParameter(string $name, mixed $default = null): mixed
292
    {
293
        return ArrayHelper::getValue($this->parameters, $name, $default);
294
    }
295
296
    /**
297
     * Set parameter value.
298
     *
299
     * @param string $name Parameter name.
300
     * @param mixed $value Parameter value.
301
     *
302
     * @return $this The same instance of validation context.
303
     */
304
    public function setParameter(string $name, mixed $value): self
305
    {
306
        $this->parameters[$name] = $value;
307
        return $this;
308
    }
309
310
    /**
311
     * Check whether {@see $attribute} is missing in a {@see $dataSet}.
312
     *
313
     * @return bool Whether {@see $attribute} is missing in a {@see $dataSet}.
314
     */
315
    public function isAttributeMissing(): bool
316
    {
317
        return $this->isDataSetMissing
318
            || ($this->attribute !== null && !$this->getDataSet()->hasAttribute($this->attribute));
319
    }
320
321
    /**
322
     * Ensure that validator is set in validation context.
323
     *
324
     * @psalm-assert ValidatorInterface $this->validator
325
     * @psalm-assert DataSetInterface $this->globalDataSet
326
     *
327
     * @throws RuntimeException If validator is not set in validation context.
328
     */
329
    private function requireValidator(): void
330
    {
331
        if ($this->validator === null) {
332
            throw new RuntimeException('Validator is not set in validation context.');
333
        }
334
    }
335
}
336