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

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

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