Passed
Pull Request — master (#560)
by
unknown
03:03
created

ValidationContext::validate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 7
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 13
ccs 0
cts 0
cp 0
crap 2
rs 10
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 the attribute belongs to.
43 503
     * `null` if data set was not set with {@see setContextDataOnce()} yet.
44
     */
45 503
    private ?DataSetInterface $dataSet = null;
46
47
    /**
48
     * @var DataSetInterface|null Current scope's data set the attribute belongs to (for example, when using with
49
     * {@see Nested} rule).
50
     * `null` if data set was not set with {@see setIsolatedDataSet()} yet.
51 140
     */
52
    private ?DataSetInterface $isolatedDataSet = null;
53 140
54 140
    /**
55
     * @var string|null Validated data set's attribute name. `null` if a single value is validated.
56
     */
57
    private ?string $attribute = null;
58
59
    /**
60 3
     * @var AttributeTranslatorInterface|null Default attribute translator to use if attribute translator is not set.
61
     */
62 3
    private ?AttributeTranslatorInterface $defaultAttributeTranslator = null;
63
64
    /**
65
     * @param array $parameters Arbitrary parameters.
66
     * @param AttributeTranslatorInterface|null $attributeTranslator Optional attribute translator instance to use.
67
     * If `null` is provided, or it's not specified, a default translator passed through
68
     * {@see setContextDataOnce()} is used.
69
     */
70
    public function __construct(
71
        private array $parameters = [],
72
        private ?AttributeTranslatorInterface $attributeTranslator = null,
73
    ) {
74
    }
75 6
76
    /**
77 6
     * Set context data if it is not set yet.
78
     *
79
     * @param ValidatorInterface $validator A validator instance.
80 503
     * @param AttributeTranslatorInterface $attributeTranslator Attribute translator to use by default. If translator
81
     * is specified via {@see setAttributeTranslator()}, it will be used instead.
82 503
     * @param mixed $rawData The raw validated data.
83
     *
84
     * @internal
85 786
     *
86
     * @return $this The same instance of validation context.
87 786
     */
88
    public function setContextDataOnce(
89
        ValidatorInterface $validator,
90
        AttributeTranslatorInterface $attributeTranslator,
91
        mixed $rawData,
92
        DataSetInterface $dataSet
93
    ): self {
94
        if ($this->validator !== null) {
95
            return $this;
96
        }
97
98
        $this->validator = $validator;
99
        $this->defaultAttributeTranslator = $attributeTranslator;
100
        $this->rawData = $rawData;
101
        $this->dataSet = $dataSet;
102
103
        return $this;
104
    }
105
106
    /**
107
     * Set attribute translator to use.
108
     *
109
     * @param AttributeTranslatorInterface|null $attributeTranslator Attribute translator to use. If `null`,
110
     * translator passed in {@see setContextData()} will be used.
111
     *
112
     * @return $this The same instance of validation context.
113
     */
114
    public function setAttributeTranslator(?AttributeTranslatorInterface $attributeTranslator): self
115
    {
116
        $this->attributeTranslator = $attributeTranslator;
117
        return $this;
118
    }
119
120
    /**
121
     * Validate data in current context.
122
     *
123
     * @param mixed $data Data set to validate. If {@see RulesProviderInterface} instance provided and rules are
124
     * not specified explicitly, they are read from the {@see RulesProviderInterface::getRules()}.
125
     * @param callable|iterable|object|string|null $rules Rules to apply. If specified, rules are not read from data set
126
     * even if it is an instance of {@see RulesProviderInterface}.
127
     *
128
     * @psalm-param RulesType $rules
129
     *
130
     * @throws RuntimeException If validator is not set in validation context.
131
     *
132
     * @return Result Validation result.
133
     */
134
    public function validate(mixed $data, callable|iterable|object|string|null $rules = null): Result
135
    {
136
        $this->requireValidator();
137
138
        $currentIsolatedDataSet = $this->isolatedDataSet;
139
        $currentAttribute = $this->attribute;
140
141
        $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

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