308 lines
7.9 KiB
PHP
Executable file
308 lines
7.9 KiB
PHP
Executable file
<?php
|
|
|
|
namespace Illuminate\Validation;
|
|
|
|
use Closure;
|
|
use Illuminate\Contracts\Validation\Rule as RuleContract;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\Rules\Exists;
|
|
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
|
|
* @return void
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
return $rules;
|
|
}
|
|
|
|
/**
|
|
* Explode the explicit rule into an array if necessary.
|
|
*
|
|
* @param mixed $rule
|
|
* @return array
|
|
*/
|
|
protected function explodeExplicitRule($rule)
|
|
{
|
|
if (is_string($rule)) {
|
|
return explode('|', $rule);
|
|
} elseif (is_object($rule)) {
|
|
return [$this->prepareRule($rule)];
|
|
}
|
|
|
|
return array_map([$this, 'prepareRule'], $rule);
|
|
}
|
|
|
|
/**
|
|
* Prepare the given rule for the Validator.
|
|
*
|
|
* @param mixed $rule
|
|
* @return mixed
|
|
*/
|
|
protected function prepareRule($rule)
|
|
{
|
|
if ($rule instanceof Closure) {
|
|
$rule = new ClosureValidationRule($rule);
|
|
}
|
|
|
|
if (! is_object($rule) ||
|
|
$rule instanceof RuleContract ||
|
|
($rule instanceof Exists && $rule->queryCallbacks()) ||
|
|
($rule instanceof Unique && $rule->queryCallbacks())) {
|
|
return $rule;
|
|
}
|
|
|
|
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) {
|
|
$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]) : [], $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) {
|
|
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 (strpos($rule, ':') !== false) {
|
|
[$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)
|
|
{
|
|
$rule = strtolower($rule);
|
|
|
|
if (in_array($rule, ['regex', 'not_regex', 'notregex'], true)) {
|
|
return [$parameter];
|
|
}
|
|
|
|
return str_getcsv($parameter);
|
|
}
|
|
|
|
/**
|
|
* Normalizes a rule so that we can accept short types.
|
|
*
|
|
* @param string $rule
|
|
* @return string
|
|
*/
|
|
protected static function normalizeRule($rule)
|
|
{
|
|
switch ($rule) {
|
|
case 'Int':
|
|
return 'Integer';
|
|
case 'Bool':
|
|
return 'Boolean';
|
|
default:
|
|
return $rule;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Expand and conditional rules in the given array of rules.
|
|
*
|
|
* @param array $rules
|
|
* @param array $data
|
|
* @return array
|
|
*/
|
|
public static function filterConditionalRules($rules, array $data = [])
|
|
{
|
|
return collect($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())
|
|
: array_filter($attributeRules->defaultRules()), ];
|
|
}
|
|
|
|
return [$attribute => collect($attributeRules)->map(function ($rule) use ($data) {
|
|
if (! $rule instanceof ConditionalRules) {
|
|
return [$rule];
|
|
}
|
|
|
|
return $rule->passes($data) ? $rule->rules() : $rule->defaultRules();
|
|
})->filter()->flatten(1)->values()->all()];
|
|
})->all();
|
|
}
|
|
}
|