Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Validation;
4
 
5
use Closure;
6
use Illuminate\Contracts\Validation\InvokableRule;
7
use Illuminate\Contracts\Validation\Rule as RuleContract;
8
use Illuminate\Support\Arr;
9
use Illuminate\Support\Str;
10
use Illuminate\Validation\Rules\Exists;
11
use Illuminate\Validation\Rules\Unique;
12
 
13
class ValidationRuleParser
14
{
15
    /**
16
     * The data being validated.
17
     *
18
     * @var array
19
     */
20
    public $data;
21
 
22
    /**
23
     * The implicit attributes.
24
     *
25
     * @var array
26
     */
27
    public $implicitAttributes = [];
28
 
29
    /**
30
     * Create a new validation rule parser.
31
     *
32
     * @param  array  $data
33
     * @return void
34
     */
35
    public function __construct(array $data)
36
    {
37
        $this->data = $data;
38
    }
39
 
40
    /**
41
     * Parse the human-friendly rules into a full rules array for the validator.
42
     *
43
     * @param  array  $rules
44
     * @return \stdClass
45
     */
46
    public function explode($rules)
47
    {
48
        $this->implicitAttributes = [];
49
 
50
        $rules = $this->explodeRules($rules);
51
 
52
        return (object) [
53
            'rules' => $rules,
54
            'implicitAttributes' => $this->implicitAttributes,
55
        ];
56
    }
57
 
58
    /**
59
     * Explode the rules into an array of explicit rules.
60
     *
61
     * @param  array  $rules
62
     * @return array
63
     */
64
    protected function explodeRules($rules)
65
    {
66
        foreach ($rules as $key => $rule) {
67
            if (str_contains($key, '*')) {
68
                $rules = $this->explodeWildcardRules($rules, $key, [$rule]);
69
 
70
                unset($rules[$key]);
71
            } else {
72
                $rules[$key] = $this->explodeExplicitRule($rule, $key);
73
            }
74
        }
75
 
76
        return $rules;
77
    }
78
 
79
    /**
80
     * Explode the explicit rule into an array if necessary.
81
     *
82
     * @param  mixed  $rule
83
     * @param  string  $attribute
84
     * @return array
85
     */
86
    protected function explodeExplicitRule($rule, $attribute)
87
    {
88
        if (is_string($rule)) {
89
            return explode('|', $rule);
90
        }
91
 
92
        if (is_object($rule)) {
93
            return Arr::wrap($this->prepareRule($rule, $attribute));
94
        }
95
 
96
        return array_map(
97
            [$this, 'prepareRule'],
98
            $rule,
99
            array_fill((int) array_key_first($rule), count($rule), $attribute)
100
        );
101
    }
102
 
103
    /**
104
     * Prepare the given rule for the Validator.
105
     *
106
     * @param  mixed  $rule
107
     * @param  string  $attribute
108
     * @return mixed
109
     */
110
    protected function prepareRule($rule, $attribute)
111
    {
112
        if ($rule instanceof Closure) {
113
            $rule = new ClosureValidationRule($rule);
114
        }
115
 
116
        if ($rule instanceof InvokableRule) {
117
            $rule = InvokableValidationRule::make($rule);
118
        }
119
 
120
        if (! is_object($rule) ||
121
            $rule instanceof RuleContract ||
122
            ($rule instanceof Exists && $rule->queryCallbacks()) ||
123
            ($rule instanceof Unique && $rule->queryCallbacks())) {
124
            return $rule;
125
        }
126
 
127
        if ($rule instanceof NestedRules) {
128
            return $rule->compile(
129
                $attribute, $this->data[$attribute] ?? null, Arr::dot($this->data)
130
            )->rules[$attribute];
131
        }
132
 
133
        return (string) $rule;
134
    }
135
 
136
    /**
137
     * Define a set of rules that apply to each element in an array attribute.
138
     *
139
     * @param  array  $results
140
     * @param  string  $attribute
141
     * @param  string|array  $rules
142
     * @return array
143
     */
144
    protected function explodeWildcardRules($results, $attribute, $rules)
145
    {
146
        $pattern = str_replace('\*', '[^\.]*', preg_quote($attribute));
147
 
148
        $data = ValidationData::initializeAndGatherData($attribute, $this->data);
149
 
150
        foreach ($data as $key => $value) {
151
            if (Str::startsWith($key, $attribute) || (bool) preg_match('/^'.$pattern.'\z/', $key)) {
152
                foreach ((array) $rules as $rule) {
153
                    if ($rule instanceof NestedRules) {
154
                        $compiled = $rule->compile($key, $value, $data);
155
 
156
                        $this->implicitAttributes = array_merge_recursive(
157
                            $compiled->implicitAttributes,
158
                            $this->implicitAttributes,
159
                            [$attribute => [$key]]
160
                        );
161
 
162
                        $results = $this->mergeRules($results, $compiled->rules);
163
                    } else {
164
                        $this->implicitAttributes[$attribute][] = $key;
165
 
166
                        $results = $this->mergeRules($results, $key, $rule);
167
                    }
168
                }
169
            }
170
        }
171
 
172
        return $results;
173
    }
174
 
175
    /**
176
     * Merge additional rules into a given attribute(s).
177
     *
178
     * @param  array  $results
179
     * @param  string|array  $attribute
180
     * @param  string|array  $rules
181
     * @return array
182
     */
183
    public function mergeRules($results, $attribute, $rules = [])
184
    {
185
        if (is_array($attribute)) {
186
            foreach ((array) $attribute as $innerAttribute => $innerRules) {
187
                $results = $this->mergeRulesForAttribute($results, $innerAttribute, $innerRules);
188
            }
189
 
190
            return $results;
191
        }
192
 
193
        return $this->mergeRulesForAttribute(
194
            $results, $attribute, $rules
195
        );
196
    }
197
 
198
    /**
199
     * Merge additional rules into a given attribute.
200
     *
201
     * @param  array  $results
202
     * @param  string  $attribute
203
     * @param  string|array  $rules
204
     * @return array
205
     */
206
    protected function mergeRulesForAttribute($results, $attribute, $rules)
207
    {
208
        $merge = head($this->explodeRules([$rules]));
209
 
210
        $results[$attribute] = array_merge(
211
            isset($results[$attribute]) ? $this->explodeExplicitRule($results[$attribute], $attribute) : [], $merge
212
        );
213
 
214
        return $results;
215
    }
216
 
217
    /**
218
     * Extract the rule name and parameters from a rule.
219
     *
220
     * @param  array|string  $rule
221
     * @return array
222
     */
223
    public static function parse($rule)
224
    {
225
        if ($rule instanceof RuleContract || $rule instanceof NestedRules) {
226
            return [$rule, []];
227
        }
228
 
229
        if (is_array($rule)) {
230
            $rule = static::parseArrayRule($rule);
231
        } else {
232
            $rule = static::parseStringRule($rule);
233
        }
234
 
235
        $rule[0] = static::normalizeRule($rule[0]);
236
 
237
        return $rule;
238
    }
239
 
240
    /**
241
     * Parse an array based rule.
242
     *
243
     * @param  array  $rule
244
     * @return array
245
     */
246
    protected static function parseArrayRule(array $rule)
247
    {
248
        return [Str::studly(trim(Arr::get($rule, 0, ''))), array_slice($rule, 1)];
249
    }
250
 
251
    /**
252
     * Parse a string based rule.
253
     *
254
     * @param  string  $rule
255
     * @return array
256
     */
257
    protected static function parseStringRule($rule)
258
    {
259
        $parameters = [];
260
 
261
        // The format for specifying validation rules and parameters follows an
262
        // easy {rule}:{parameters} formatting convention. For instance the
263
        // rule "Max:3" states that the value may only be three letters.
264
        if (str_contains($rule, ':')) {
265
            [$rule, $parameter] = explode(':', $rule, 2);
266
 
267
            $parameters = static::parseParameters($rule, $parameter);
268
        }
269
 
270
        return [Str::studly(trim($rule)), $parameters];
271
    }
272
 
273
    /**
274
     * Parse a parameter list.
275
     *
276
     * @param  string  $rule
277
     * @param  string  $parameter
278
     * @return array
279
     */
280
    protected static function parseParameters($rule, $parameter)
281
    {
282
        return static::ruleIsRegex($rule) ? [$parameter] : str_getcsv($parameter);
283
    }
284
 
285
    /**
286
     * Determine if the rule is a regular expression.
287
     *
288
     * @param  string  $rule
289
     * @return bool
290
     */
291
    protected static function ruleIsRegex($rule)
292
    {
293
        return in_array(strtolower($rule), ['regex', 'not_regex', 'notregex'], true);
294
    }
295
 
296
    /**
297
     * Normalizes a rule so that we can accept short types.
298
     *
299
     * @param  string  $rule
300
     * @return string
301
     */
302
    protected static function normalizeRule($rule)
303
    {
304
        return match ($rule) {
305
            'Int' => 'Integer',
306
            'Bool' => 'Boolean',
307
            default => $rule,
308
        };
309
    }
310
 
311
    /**
312
     * Expand and conditional rules in the given array of rules.
313
     *
314
     * @param  array  $rules
315
     * @param  array  $data
316
     * @return array
317
     */
318
    public static function filterConditionalRules($rules, array $data = [])
319
    {
320
        return collect($rules)->mapWithKeys(function ($attributeRules, $attribute) use ($data) {
321
            if (! is_array($attributeRules) &&
322
                ! $attributeRules instanceof ConditionalRules) {
323
                return [$attribute => $attributeRules];
324
            }
325
 
326
            if ($attributeRules instanceof ConditionalRules) {
327
                return [$attribute => $attributeRules->passes($data)
328
                                ? array_filter($attributeRules->rules($data))
329
                                : array_filter($attributeRules->defaultRules($data)), ];
330
            }
331
 
332
            return [$attribute => collect($attributeRules)->map(function ($rule) use ($data) {
333
                if (! $rule instanceof ConditionalRules) {
334
                    return [$rule];
335
                }
336
 
337
                return $rule->passes($data) ? $rule->rules($data) : $rule->defaultRules($data);
338
            })->filter()->flatten(1)->values()->all()];
339
        })->all();
340
    }
341
}