| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- <?php
- namespace Illuminate\Validation;
- use Closure;
- use Illuminate\Contracts\Validation\CompilableRules;
- use Illuminate\Contracts\Validation\InvokableRule;
- use Illuminate\Contracts\Validation\Rule as RuleContract;
- use Illuminate\Contracts\Validation\ValidationRule;
- use Illuminate\Support\Arr;
- use Illuminate\Support\Collection;
- use Illuminate\Support\Str;
- use Illuminate\Validation\Rules\Date;
- use Illuminate\Validation\Rules\Exists;
- use Illuminate\Validation\Rules\Numeric;
- use Illuminate\Validation\Rules\StringRule;
- use Illuminate\Validation\Rules\Unique;
- class ValidationRuleParser
- {
- /**
- * The data being validated.
- *
- * @var array
- */
- public $data;
- /**
- * The implicit attributes.
- *
- * @var array
- */
- public $implicitAttributes = [];
- /**
- * Create a new validation rule parser.
- *
- * @param array $data
- */
- public function __construct(array $data)
- {
- $this->data = $data;
- }
- /**
- * Parse the human-friendly rules into a full rules array for the validator.
- *
- * @param array $rules
- * @return \stdClass
- */
- public function explode($rules)
- {
- $this->implicitAttributes = [];
- $rules = $this->explodeRules($rules);
- return (object) [
- 'rules' => $rules,
- 'implicitAttributes' => $this->implicitAttributes,
- ];
- }
- /**
- * Explode the rules into an array of explicit rules.
- *
- * @param array $rules
- * @return array
- */
- protected function explodeRules($rules)
- {
- foreach ($rules as $key => $rule) {
- if (str_contains($key, '*')) {
- $rules = $this->explodeWildcardRules($rules, $key, [$rule]);
- unset($rules[$key]);
- } else {
- $rules[$key] = $this->explodeExplicitRule($rule, $key);
- }
- }
- return $rules;
- }
- /**
- * Explode the explicit rule into an array if necessary.
- *
- * @param mixed $rule
- * @param string $attribute
- * @return array
- */
- protected function explodeExplicitRule($rule, $attribute)
- {
- if (is_string($rule)) {
- return explode('|', $rule);
- }
- if (is_object($rule)) {
- if ($rule instanceof Date || $rule instanceof Numeric || $rule instanceof StringRule) {
- return explode('|', (string) $rule);
- }
- return Arr::wrap($this->prepareRule($rule, $attribute));
- }
- $rules = [];
- foreach ($rule as $value) {
- if ($value instanceof Date || $value instanceof Numeric || $value instanceof StringRule) {
- $rules = array_merge($rules, explode('|', (string) $value));
- } else {
- $rules[] = $this->prepareRule($value, $attribute);
- }
- }
- return $rules;
- }
- /**
- * Prepare the given rule for the Validator.
- *
- * @param mixed $rule
- * @param string $attribute
- * @return mixed
- */
- protected function prepareRule($rule, $attribute)
- {
- if ($rule instanceof Closure) {
- $rule = new ClosureValidationRule($rule);
- }
- if ($rule instanceof InvokableRule || $rule instanceof ValidationRule) {
- $rule = InvokableValidationRule::make($rule);
- }
- if (! is_object($rule) ||
- $rule instanceof RuleContract ||
- ($rule instanceof Exists && $rule->queryCallbacks()) ||
- ($rule instanceof Unique && $rule->queryCallbacks())) {
- return $rule;
- }
- if ($rule instanceof CompilableRules) {
- return $rule->compile(
- $attribute, $this->data[$attribute] ?? null, Arr::dot($this->data), $this->data
- )->rules[$attribute];
- }
- return (string) $rule;
- }
- /**
- * Define a set of rules that apply to each element in an array attribute.
- *
- * @param array $results
- * @param string $attribute
- * @param string|array $rules
- * @return array
- */
- protected function explodeWildcardRules($results, $attribute, $rules)
- {
- $pattern = str_replace('\*', '[^\.]*', preg_quote($attribute, '/'));
- $data = ValidationData::initializeAndGatherData($attribute, $this->data);
- foreach ($data as $key => $value) {
- if (Str::startsWith($key, $attribute) || (bool) preg_match('/^'.$pattern.'\z/', $key)) {
- foreach ((array) $rules as $rule) {
- if ($rule instanceof CompilableRules) {
- $context = Arr::get($this->data, Str::beforeLast($key, '.'));
- $compiled = $rule->compile($key, $value, $data, $context);
- $this->implicitAttributes = array_merge_recursive(
- $compiled->implicitAttributes,
- $this->implicitAttributes,
- [$attribute => [$key]]
- );
- $results = $this->mergeRules($results, $compiled->rules);
- } else {
- $this->implicitAttributes[$attribute][] = $key;
- $results = $this->mergeRules($results, $key, $rule);
- }
- }
- }
- }
- return $results;
- }
- /**
- * Merge additional rules into a given attribute(s).
- *
- * @param array $results
- * @param string|array $attribute
- * @param string|array $rules
- * @return array
- */
- public function mergeRules($results, $attribute, $rules = [])
- {
- if (is_array($attribute)) {
- foreach ((array) $attribute as $innerAttribute => $innerRules) {
- $results = $this->mergeRulesForAttribute($results, $innerAttribute, $innerRules);
- }
- return $results;
- }
- return $this->mergeRulesForAttribute(
- $results, $attribute, $rules
- );
- }
- /**
- * Merge additional rules into a given attribute.
- *
- * @param array $results
- * @param string $attribute
- * @param string|array $rules
- * @return array
- */
- protected function mergeRulesForAttribute($results, $attribute, $rules)
- {
- $merge = head($this->explodeRules([$rules]));
- $results[$attribute] = array_merge(
- isset($results[$attribute]) ? $this->explodeExplicitRule($results[$attribute], $attribute) : [], $merge
- );
- return $results;
- }
- /**
- * Extract the rule name and parameters from a rule.
- *
- * @param array|string $rule
- * @return array
- */
- public static function parse($rule)
- {
- if ($rule instanceof RuleContract || $rule instanceof CompilableRules) {
- return [$rule, []];
- }
- if (is_array($rule)) {
- $rule = static::parseArrayRule($rule);
- } else {
- $rule = static::parseStringRule($rule);
- }
- $rule[0] = static::normalizeRule($rule[0]);
- return $rule;
- }
- /**
- * Parse an array based rule.
- *
- * @param array $rule
- * @return array
- */
- protected static function parseArrayRule(array $rule)
- {
- return [Str::studly(trim(Arr::get($rule, 0, ''))), array_slice($rule, 1)];
- }
- /**
- * Parse a string based rule.
- *
- * @param string $rule
- * @return array
- */
- protected static function parseStringRule($rule)
- {
- $parameters = [];
- // The format for specifying validation rules and parameters follows an
- // easy {rule}:{parameters} formatting convention. For instance the
- // rule "Max:3" states that the value may only be three letters.
- if (str_contains($rule, ':')) {
- [$rule, $parameter] = explode(':', $rule, 2);
- $parameters = static::parseParameters($rule, $parameter);
- }
- return [Str::studly(trim($rule)), $parameters];
- }
- /**
- * Parse a parameter list.
- *
- * @param string $rule
- * @param string $parameter
- * @return array
- */
- protected static function parseParameters($rule, $parameter)
- {
- return static::ruleIsRegex($rule) ? [$parameter] : str_getcsv($parameter, escape: '\\');
- }
- /**
- * Determine if the rule is a regular expression.
- *
- * @param string $rule
- * @return bool
- */
- protected static function ruleIsRegex($rule)
- {
- return in_array(strtolower($rule), ['regex', 'not_regex', 'notregex'], true);
- }
- /**
- * Normalizes a rule so that we can accept short types.
- *
- * @param string $rule
- * @return string
- */
- protected static function normalizeRule($rule)
- {
- return match ($rule) {
- 'Int' => 'Integer',
- 'Bool' => 'Boolean',
- default => $rule,
- };
- }
- /**
- * Expand the conditional rules in the given array of rules.
- *
- * @param array $rules
- * @param array $data
- * @return array
- */
- public static function filterConditionalRules($rules, array $data = [])
- {
- return (new Collection($rules))->mapWithKeys(function ($attributeRules, $attribute) use ($data) {
- if (! is_array($attributeRules) &&
- ! $attributeRules instanceof ConditionalRules) {
- return [$attribute => $attributeRules];
- }
- if ($attributeRules instanceof ConditionalRules) {
- return [$attribute => $attributeRules->passes($data)
- ? array_filter($attributeRules->rules($data))
- : array_filter($attributeRules->defaultRules($data)), ];
- }
- return [$attribute => (new Collection($attributeRules))->map(function ($rule) use ($data) {
- if (! $rule instanceof ConditionalRules) {
- return [$rule];
- }
- return $rule->passes($data) ? $rule->rules($data) : $rule->defaultRules($data);
- })->filter()->flatten(1)->values()->all()];
- })->all();
- }
- }
|