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

ValidationContext::getGlobalDataSet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

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