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\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 $globalDataSet = 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 setDataSet()} yet.
51 140
     */
52
    private ?DataSetInterface $dataSet = 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
     * @param DataSetInterface $dataSet Global data set ({@see $globalDataSet}.
84
     *
85 786
     * @internal
86
     *
87 786
     * @return $this The same instance of validation context.
88
     */
89
    public function setContextDataOnce(
90
        ValidatorInterface $validator,
91
        AttributeTranslatorInterface $attributeTranslator,
92
        mixed $rawData,
93
        DataSetInterface $dataSet,
94
    ): self {
95
        if ($this->validator !== null) {
96
            return $this;
97
        }
98
99
        $this->validator = $validator;
100
        $this->defaultAttributeTranslator = $attributeTranslator;
101
        $this->rawData = $rawData;
102
        $this->globalDataSet = $dataSet;
103
104
        return $this;
105
    }
106
107
    /**
108
     * Set attribute translator to use.
109
     *
110
     * @param AttributeTranslatorInterface|null $attributeTranslator Attribute translator to use. If `null`,
111
     * translator passed in {@see setContextData()} will be used.
112
     *
113
     * @return $this The same instance of validation context.
114
     */
115
    public function setAttributeTranslator(?AttributeTranslatorInterface $attributeTranslator): self
116
    {
117
        $this->attributeTranslator = $attributeTranslator;
118
        return $this;
119
    }
120
121
    /**
122
     * Validate data in current context.
123
     *
124
     * @param mixed $data Data set to validate. If {@see RulesProviderInterface} instance provided and rules are
125
     * not specified explicitly, they are read from the {@see RulesProviderInterface::getRules()}.
126
     * @param callable|iterable|object|string|null $rules Rules to apply. If specified, rules are not read from data set
127
     * even if it is an instance of {@see RulesProviderInterface}.
128
     *
129
     * @psalm-param RulesType $rules
130
     *
131
     * @throws RuntimeException If validator is not set in validation context.
132
     *
133
     * @return Result Validation result.
134
     */
135
    public function validate(mixed $data, callable|iterable|object|string|null $rules = null): Result
136
    {
137
        $this->requireValidator();
138
139
        $currentIsolatedDataSet = $this->dataSet;
140
        $currentAttribute = $this->attribute;
141
142
        $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

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