/vendor/laravel/framework/src/Illuminate/Validation/Validator.php
PHP | 1719 lines | 725 code | 247 blank | 747 comment | 109 complexity | 59fc5ccf6d06582c70bd8c1bd83cc264 MD5 | raw file
- <?php
- namespace Illuminate\Validation;
- use Closure;
- use DateTime;
- use Countable;
- use Exception;
- use DateTimeZone;
- use RuntimeException;
- use Illuminate\Support\Arr;
- use Illuminate\Support\Str;
- use BadMethodCallException;
- use InvalidArgumentException;
- use Illuminate\Support\Fluent;
- use Illuminate\Support\MessageBag;
- use Illuminate\Contracts\Container\Container;
- use Symfony\Component\HttpFoundation\File\File;
- use Symfony\Component\Translation\TranslatorInterface;
- use Symfony\Component\HttpFoundation\File\UploadedFile;
- use Illuminate\Contracts\Validation\Validator as ValidatorContract;
- class Validator implements ValidatorContract
- {
- /**
- * The Translator implementation.
- *
- * @var \Symfony\Component\Translation\TranslatorInterface
- */
- protected $translator;
- /**
- * The Presence Verifier implementation.
- *
- * @var \Illuminate\Validation\PresenceVerifierInterface
- */
- protected $presenceVerifier;
- /**
- * The container instance.
- *
- * @var \Illuminate\Contracts\Container\Container
- */
- protected $container;
- /**
- * The failed validation rules.
- *
- * @var array
- */
- protected $failedRules = [];
- /**
- * The message bag instance.
- *
- * @var \Illuminate\Support\MessageBag
- */
- protected $messages;
- /**
- * The data under validation.
- *
- * @var array
- */
- protected $data;
- /**
- * The files under validation.
- *
- * @var array
- */
- protected $files = [];
- /**
- * The initial rules provided.
- *
- * @var array
- */
- protected $initialRules;
- /**
- * The rules to be applied to the data.
- *
- * @var array
- */
- protected $rules;
- /**
- * The array of wildcard attributes with their asterisks expanded.
- *
- * @var array
- */
- protected $implicitAttributes = [];
- /**
- * All of the registered "after" callbacks.
- *
- * @var array
- */
- protected $after = [];
- /**
- * The array of custom error messages.
- *
- * @var array
- */
- protected $customMessages = [];
- /**
- * The array of fallback error messages.
- *
- * @var array
- */
- protected $fallbackMessages = [];
- /**
- * The array of custom attribute names.
- *
- * @var array
- */
- protected $customAttributes = [];
- /**
- * The array of custom displayabled values.
- *
- * @var array
- */
- protected $customValues = [];
- /**
- * All of the custom validator extensions.
- *
- * @var array
- */
- protected $extensions = [];
- /**
- * All of the custom replacer extensions.
- *
- * @var array
- */
- protected $replacers = [];
- /**
- * The size related validation rules.
- *
- * @var array
- */
- protected $sizeRules = ['Size', 'Between', 'Min', 'Max'];
- /**
- * The numeric related validation rules.
- *
- * @var array
- */
- protected $numericRules = ['Numeric', 'Integer'];
- /**
- * The validation rules that imply the field is required.
- *
- * @var array
- */
- protected $implicitRules = [
- 'Required', 'Filled', 'RequiredWith', 'RequiredWithAll', 'RequiredWithout', 'RequiredWithoutAll',
- 'RequiredIf', 'RequiredUnless', 'Accepted', 'Present',
- // 'Array', 'Boolean', 'Integer', 'Numeric', 'String',
- ];
- /**
- * The validation rules which depend on other fields as parameters.
- *
- * @var array
- */
- protected $dependentRules = [
- 'RequiredWith', 'RequiredWithAll', 'RequiredWithout', 'RequiredWithoutAll',
- 'RequiredIf', 'RequiredUnless', 'Confirmed', 'Same', 'Different', 'Unique',
- ];
- /**
- * Create a new Validator instance.
- *
- * @param \Symfony\Component\Translation\TranslatorInterface $translator
- * @param array $data
- * @param array $rules
- * @param array $messages
- * @param array $customAttributes
- * @return void
- */
- public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
- {
- $this->translator = $translator;
- $this->customMessages = $messages;
- $this->data = $this->parseData($data);
- $this->customAttributes = $customAttributes;
- $this->initialRules = $rules;
- $this->setRules($rules);
- }
- /**
- * Parse the data and hydrate the files array.
- *
- * @param array $data
- * @param string $arrayKey
- * @return array
- */
- protected function parseData(array $data, $arrayKey = null)
- {
- if (is_null($arrayKey)) {
- $this->files = [];
- }
- foreach ($data as $key => $value) {
- $key = ($arrayKey) ? "$arrayKey.$key" : $key;
- // If this value is an instance of the HttpFoundation File class we will
- // remove it from the data array and add it to the files array, which
- // we use to conveniently separate out these files from other data.
- if ($value instanceof File) {
- $this->files[$key] = $value;
- unset($data[$key]);
- } elseif (is_array($value)) {
- $this->parseData($value, $key);
- }
- }
- return $data;
- }
- /**
- * Explode the rules into an array of rules.
- *
- * @param string|array $rules
- * @return array
- */
- protected function explodeRules($rules)
- {
- foreach ($rules as $key => $rule) {
- if (Str::contains($key, '*')) {
- $this->each($key, [$rule]);
- unset($rules[$key]);
- } else {
- $rules[$key] = (is_string($rule)) ? explode('|', $rule) : $rule;
- }
- }
- return $rules;
- }
- /**
- * After an after validation callback.
- *
- * @param callable|string $callback
- * @return $this
- */
- public function after($callback)
- {
- $this->after[] = function () use ($callback) {
- return call_user_func_array($callback, [$this]);
- };
- return $this;
- }
- /**
- * Add conditions to a given field based on a Closure.
- *
- * @param string $attribute
- * @param string|array $rules
- * @param callable $callback
- * @return void
- */
- public function sometimes($attribute, $rules, callable $callback)
- {
- $payload = new Fluent($this->attributes());
- if (call_user_func($callback, $payload)) {
- foreach ((array) $attribute as $key) {
- $this->mergeRules($key, $rules);
- }
- }
- }
- /**
- * Define a set of rules that apply to each element in an array attribute.
- *
- * @param string $attribute
- * @param string|array $rules
- * @return void
- *
- * @throws \InvalidArgumentException
- */
- public function each($attribute, $rules)
- {
- $data = Arr::dot($this->initializeAttributeOnData($attribute));
- $pattern = str_replace('\*', '[^\.]+', preg_quote($attribute));
- $data = array_merge($data, $this->extractValuesForWildcards(
- $data, $attribute
- ));
- foreach ($data as $key => $value) {
- if (Str::startsWith($key, $attribute) || (bool) preg_match('/^'.$pattern.'\z/', $key)) {
- foreach ((array) $rules as $ruleKey => $ruleValue) {
- if (! is_string($ruleKey) || Str::endsWith($key, $ruleKey)) {
- $this->implicitAttributes[$attribute][] = $key;
- $this->mergeRules($key, $ruleValue);
- }
- }
- }
- }
- }
- /**
- * Gather a copy of the attribute data filled with any missing attributes.
- *
- * @param string $attribute
- * @return array
- */
- protected function initializeAttributeOnData($attribute)
- {
- $explicitPath = $this->getLeadingExplicitAttributePath($attribute);
- $data = $this->extractDataFromPath($explicitPath);
- if (! Str::contains($attribute, '*') || Str::endsWith($attribute, '*')) {
- return $data;
- }
- return data_fill($data, $attribute, null);
- }
- /**
- * Get all of the exact attribute values for a given wildcard attribute.
- *
- * @param array $data
- * @param string $attribute
- * @return array
- */
- public function extractValuesForWildcards($data, $attribute)
- {
- $keys = [];
- $pattern = str_replace('\*', '[^\.]+', preg_quote($attribute));
- foreach ($data as $key => $value) {
- if ((bool) preg_match('/^'.$pattern.'/', $key, $matches)) {
- $keys[] = $matches[0];
- }
- }
- $keys = array_unique($keys);
- $data = [];
- foreach ($keys as $key) {
- $data[$key] = array_get($this->data, $key);
- }
- return $data;
- }
- /**
- * Merge additional rules into a given attribute(s).
- *
- * @param string $attribute
- * @param string|array $rules
- * @return $this
- */
- public function mergeRules($attribute, $rules = [])
- {
- if (is_array($attribute)) {
- foreach ($attribute as $innerAttribute => $innerRules) {
- $this->mergeRulesForAttribute($innerAttribute, $innerRules);
- }
- return $this;
- }
- return $this->mergeRulesForAttribute($attribute, $rules);
- }
- /**
- * Merge additional rules into a given attribute.
- *
- * @param string $attribute
- * @param string|array $rules
- * @return $this
- */
- protected function mergeRulesForAttribute($attribute, $rules)
- {
- $current = isset($this->rules[$attribute]) ? $this->rules[$attribute] : [];
- $merge = head($this->explodeRules([$rules]));
- $this->rules[$attribute] = array_merge($current, $merge);
- return $this;
- }
- /**
- * Determine if the data passes the validation rules.
- *
- * @return bool
- */
- public function passes()
- {
- $this->messages = new MessageBag;
- // We'll spin through each rule, validating the attributes attached to that
- // rule. Any error messages will be added to the containers with each of
- // the other error messages, returning true if we don't have messages.
- foreach ($this->rules as $attribute => $rules) {
- foreach ($rules as $rule) {
- $this->validate($attribute, $rule);
- if ($this->shouldStopValidating($attribute)) {
- break;
- }
- }
- }
- // Here we will spin through all of the "after" hooks on this validator and
- // fire them off. This gives the callbacks a chance to perform all kinds
- // of other validation that needs to get wrapped up in this operation.
- foreach ($this->after as $after) {
- call_user_func($after);
- }
- return count($this->messages->all()) === 0;
- }
- /**
- * Determine if the data fails the validation rules.
- *
- * @return bool
- */
- public function fails()
- {
- return ! $this->passes();
- }
- /**
- * Validate a given attribute against a rule.
- *
- * @param string $attribute
- * @param string $rule
- * @return void
- */
- protected function validate($attribute, $rule)
- {
- list($rule, $parameters) = $this->parseRule($rule);
- if ($rule == '') {
- return;
- }
- // First we will get the numeric keys for the given attribute in case the field is nested in
- // an array. Then we determine if the given rule accepts other field names as parameters.
- // If so, we will replace any asterisks found in the parameters with the numeric keys.
- if (($keys = $this->getNumericKeys($attribute)) &&
- $this->dependsOnOtherFields($rule)) {
- $parameters = $this->replaceAsterisksInParameters($parameters, $keys);
- }
- // We will get the value for the given attribute from the array of data and then
- // verify that the attribute is indeed validatable. Unless the rule implies
- // that the attribute is required, rules are not run for missing values.
- $value = $this->getValue($attribute);
- $validatable = $this->isValidatable($rule, $attribute, $value);
- $method = "validate{$rule}";
- if ($validatable && ! $this->$method($attribute, $value, $parameters, $this)) {
- $this->addFailure($attribute, $rule, $parameters);
- }
- }
- /**
- * Returns the data which was valid.
- *
- * @return array
- */
- public function valid()
- {
- if (! $this->messages) {
- $this->passes();
- }
- return array_diff_key($this->data, $this->messages()->toArray());
- }
- /**
- * Returns the data which was invalid.
- *
- * @return array
- */
- public function invalid()
- {
- if (! $this->messages) {
- $this->passes();
- }
- return array_intersect_key($this->data, $this->messages()->toArray());
- }
- /**
- * Get the value of a given attribute.
- *
- * @param string $attribute
- * @return mixed
- */
- protected function getValue($attribute)
- {
- if (! is_null($value = Arr::get($this->data, $attribute))) {
- return $value;
- } elseif (! is_null($value = Arr::get($this->files, $attribute))) {
- return $value;
- }
- }
- /**
- * Determine if the attribute is validatable.
- *
- * @param string $rule
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function isValidatable($rule, $attribute, $value)
- {
- return $this->presentOrRuleIsImplicit($rule, $attribute, $value) &&
- $this->passesOptionalCheck($attribute) &&
- $this->hasNotFailedPreviousRuleIfPresenceRule($rule, $attribute);
- }
- /**
- * Determine if the field is present, or the rule implies required.
- *
- * @param string $rule
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function presentOrRuleIsImplicit($rule, $attribute, $value)
- {
- return $this->validateRequired($attribute, $value) || $this->isImplicit($rule);
- }
- /**
- * Determine if the attribute passes any optional check.
- *
- * @param string $attribute
- * @return bool
- */
- protected function passesOptionalCheck($attribute)
- {
- if ($this->hasRule($attribute, ['Sometimes'])) {
- return array_key_exists($attribute, Arr::dot($this->data))
- || in_array($attribute, array_keys($this->data))
- || array_key_exists($attribute, $this->files);
- }
- return true;
- }
- /**
- * Determine if a given rule implies the attribute is required.
- *
- * @param string $rule
- * @return bool
- */
- protected function isImplicit($rule)
- {
- return in_array($rule, $this->implicitRules);
- }
- /**
- * Determine if it's a necessary presence validation.
- *
- * This is to avoid possible database type comparison errors.
- *
- * @param string $rule
- * @param string $attribute
- * @return bool
- */
- protected function hasNotFailedPreviousRuleIfPresenceRule($rule, $attribute)
- {
- return in_array($rule, ['Unique', 'Exists'])
- ? ! $this->messages->has($attribute) : true;
- }
- /**
- * Add a failed rule and error message to the collection.
- *
- * @param string $attribute
- * @param string $rule
- * @param array $parameters
- * @return void
- */
- protected function addFailure($attribute, $rule, $parameters)
- {
- $this->addError($attribute, $rule, $parameters);
- $this->failedRules[$attribute][$rule] = $parameters;
- }
- /**
- * Add an error message to the validator's collection of messages.
- *
- * @param string $attribute
- * @param string $rule
- * @param array $parameters
- * @return void
- */
- protected function addError($attribute, $rule, $parameters)
- {
- $message = $this->getMessage($attribute, $rule);
- $message = $this->doReplacements($message, $attribute, $rule, $parameters);
- $this->messages->add($attribute, $message);
- }
- /**
- * "Validate" optional attributes.
- *
- * Always returns true, just lets us put sometimes in rules.
- *
- * @return bool
- */
- protected function validateSometimes()
- {
- return true;
- }
- /**
- * "Break" on first validation fail.
- *
- * Always returns true, just lets us put "bail" in rules.
- *
- * @return bool
- */
- protected function validateBail()
- {
- return true;
- }
- /**
- * Stop on error if "bail" rule is assigned and attribute has a message.
- *
- * @param string $attribute
- * @return bool
- */
- protected function shouldStopValidating($attribute)
- {
- if (! $this->hasRule($attribute, ['Bail'])) {
- return false;
- }
- return $this->messages->has($attribute);
- }
- /**
- * Validate that a required attribute exists.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateRequired($attribute, $value)
- {
- if (is_null($value)) {
- return false;
- } elseif (is_string($value) && trim($value) === '') {
- return false;
- } elseif ((is_array($value) || $value instanceof Countable) && count($value) < 1) {
- return false;
- } elseif ($value instanceof File) {
- return (string) $value->getPath() != '';
- }
- return true;
- }
- /**
- * Validate that an attribute exists even if not filled.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validatePresent($attribute, $value)
- {
- return Arr::has($this->data, $attribute);
- }
- /**
- * Validate the given attribute is filled if it is present.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateFilled($attribute, $value)
- {
- if (Arr::has(array_merge($this->data, $this->files), $attribute)) {
- return $this->validateRequired($attribute, $value);
- }
- return true;
- }
- /**
- * Determine if any of the given attributes fail the required test.
- *
- * @param array $attributes
- * @return bool
- */
- protected function anyFailingRequired(array $attributes)
- {
- foreach ($attributes as $key) {
- if (! $this->validateRequired($key, $this->getValue($key))) {
- return true;
- }
- }
- return false;
- }
- /**
- * Determine if all of the given attributes fail the required test.
- *
- * @param array $attributes
- * @return bool
- */
- protected function allFailingRequired(array $attributes)
- {
- foreach ($attributes as $key) {
- if ($this->validateRequired($key, $this->getValue($key))) {
- return false;
- }
- }
- return true;
- }
- /**
- * Validate that an attribute exists when any other attribute exists.
- *
- * @param string $attribute
- * @param mixed $value
- * @param mixed $parameters
- * @return bool
- */
- protected function validateRequiredWith($attribute, $value, $parameters)
- {
- if (! $this->allFailingRequired($parameters)) {
- return $this->validateRequired($attribute, $value);
- }
- return true;
- }
- /**
- * Validate that an attribute exists when all other attributes exists.
- *
- * @param string $attribute
- * @param mixed $value
- * @param mixed $parameters
- * @return bool
- */
- protected function validateRequiredWithAll($attribute, $value, $parameters)
- {
- if (! $this->anyFailingRequired($parameters)) {
- return $this->validateRequired($attribute, $value);
- }
- return true;
- }
- /**
- * Validate that an attribute exists when another attribute does not.
- *
- * @param string $attribute
- * @param mixed $value
- * @param mixed $parameters
- * @return bool
- */
- protected function validateRequiredWithout($attribute, $value, $parameters)
- {
- if ($this->anyFailingRequired($parameters)) {
- return $this->validateRequired($attribute, $value);
- }
- return true;
- }
- /**
- * Validate that an attribute exists when all other attributes do not.
- *
- * @param string $attribute
- * @param mixed $value
- * @param mixed $parameters
- * @return bool
- */
- protected function validateRequiredWithoutAll($attribute, $value, $parameters)
- {
- if ($this->allFailingRequired($parameters)) {
- return $this->validateRequired($attribute, $value);
- }
- return true;
- }
- /**
- * Validate that an attribute exists when another attribute has a given value.
- *
- * @param string $attribute
- * @param mixed $value
- * @param mixed $parameters
- * @return bool
- */
- protected function validateRequiredIf($attribute, $value, $parameters)
- {
- $this->requireParameterCount(2, $parameters, 'required_if');
- $data = Arr::get($this->data, $parameters[0]);
- $values = array_slice($parameters, 1);
- if (in_array($data, $values)) {
- return $this->validateRequired($attribute, $value);
- }
- return true;
- }
- /**
- * Validate that an attribute exists when another attribute does not have a given value.
- *
- * @param string $attribute
- * @param mixed $value
- * @param mixed $parameters
- * @return bool
- */
- protected function validateRequiredUnless($attribute, $value, $parameters)
- {
- $this->requireParameterCount(2, $parameters, 'required_unless');
- $data = Arr::get($this->data, $parameters[0]);
- $values = array_slice($parameters, 1);
- if (! in_array($data, $values)) {
- return $this->validateRequired($attribute, $value);
- }
- return true;
- }
- /**
- * Get the number of attributes in a list that are present.
- *
- * @param array $attributes
- * @return int
- */
- protected function getPresentCount($attributes)
- {
- $count = 0;
- foreach ($attributes as $key) {
- if (Arr::get($this->data, $key) || Arr::get($this->files, $key)) {
- $count++;
- }
- }
- return $count;
- }
- /**
- * Validate that the values of an attribute is in another attribute.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateInArray($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'in_array');
- $explicitPath = $this->getLeadingExplicitAttributePath($parameters[0]);
- $attributeData = $this->extractDataFromPath($explicitPath);
- $otherValues = Arr::where(Arr::dot($attributeData), function ($key) use ($parameters) {
- return Str::is($parameters[0], $key);
- });
- return in_array($value, $otherValues);
- }
- /**
- * Validate that an attribute has a matching confirmation.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateConfirmed($attribute, $value)
- {
- return $this->validateSame($attribute, $value, [$attribute.'_confirmation']);
- }
- /**
- * Validate that two attributes match.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateSame($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'same');
- $other = Arr::get($this->data, $parameters[0]);
- return isset($other) && $value === $other;
- }
- /**
- * Validate that an attribute is different from another attribute.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateDifferent($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'different');
- $other = Arr::get($this->data, $parameters[0]);
- return isset($other) && $value !== $other;
- }
- /**
- * Validate that an attribute was "accepted".
- *
- * This validation rule implies the attribute is "required".
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateAccepted($attribute, $value)
- {
- $acceptable = ['yes', 'on', '1', 1, true, 'true'];
- return $this->validateRequired($attribute, $value) && in_array($value, $acceptable, true);
- }
- /**
- * Validate that an attribute is an array.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateArray($attribute, $value)
- {
- if (! $this->hasAttribute($attribute)) {
- return true;
- }
- return is_null($value) || is_array($value);
- }
- /**
- * Validate that an attribute is a boolean.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateBoolean($attribute, $value)
- {
- if (! $this->hasAttribute($attribute)) {
- return true;
- }
- $acceptable = [true, false, 0, 1, '0', '1'];
- return is_null($value) || in_array($value, $acceptable, true);
- }
- /**
- * Validate that an attribute is an integer.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateInteger($attribute, $value)
- {
- if (! $this->hasAttribute($attribute)) {
- return true;
- }
- return is_null($value) || filter_var($value, FILTER_VALIDATE_INT) !== false;
- }
- /**
- * Validate that an attribute is numeric.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateNumeric($attribute, $value)
- {
- if (! $this->hasAttribute($attribute)) {
- return true;
- }
- return is_null($value) || is_numeric($value);
- }
- /**
- * Validate that an attribute is a string.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateString($attribute, $value)
- {
- if (! $this->hasAttribute($attribute)) {
- return true;
- }
- return is_null($value) || is_string($value);
- }
- /**
- * Validate the attribute is a valid JSON string.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateJson($attribute, $value)
- {
- if (! is_scalar($value) && ! method_exists($value, '__toString')) {
- return false;
- }
- json_decode($value);
- return json_last_error() === JSON_ERROR_NONE;
- }
- /**
- * Validate that an attribute has a given number of digits.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateDigits($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'digits');
- return $this->validateNumeric($attribute, $value)
- && strlen((string) $value) == $parameters[0];
- }
- /**
- * Validate that an attribute is between a given number of digits.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateDigitsBetween($attribute, $value, $parameters)
- {
- $this->requireParameterCount(2, $parameters, 'digits_between');
- $length = strlen((string) $value);
- return $this->validateNumeric($attribute, $value)
- && $length >= $parameters[0] && $length <= $parameters[1];
- }
- /**
- * Validate the size of an attribute.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateSize($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'size');
- return $this->getSize($attribute, $value) == $parameters[0];
- }
- /**
- * Validate the size of an attribute is between a set of values.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateBetween($attribute, $value, $parameters)
- {
- $this->requireParameterCount(2, $parameters, 'between');
- $size = $this->getSize($attribute, $value);
- return $size >= $parameters[0] && $size <= $parameters[1];
- }
- /**
- * Validate the size of an attribute is greater than a minimum value.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateMin($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'min');
- return $this->getSize($attribute, $value) >= $parameters[0];
- }
- /**
- * Validate the size of an attribute is less than a maximum value.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateMax($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'max');
- if ($value instanceof UploadedFile && ! $value->isValid()) {
- return false;
- }
- return $this->getSize($attribute, $value) <= $parameters[0];
- }
- /**
- * Get the size of an attribute.
- *
- * @param string $attribute
- * @param mixed $value
- * @return mixed
- */
- protected function getSize($attribute, $value)
- {
- $hasNumeric = $this->hasRule($attribute, $this->numericRules);
- // This method will determine if the attribute is a number, string, or file and
- // return the proper size accordingly. If it is a number, then number itself
- // is the size. If it is a file, we take kilobytes, and for a string the
- // entire length of the string will be considered the attribute size.
- if (is_numeric($value) && $hasNumeric) {
- return $value;
- } elseif (is_array($value)) {
- return count($value);
- } elseif ($value instanceof File) {
- return $value->getSize() / 1024;
- }
- return mb_strlen($value);
- }
- /**
- * Validate an attribute is contained within a list of values.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateIn($attribute, $value, $parameters)
- {
- if (is_array($value) && $this->hasRule($attribute, 'Array')) {
- return count(array_diff($value, $parameters)) == 0;
- }
- return ! is_array($value) && in_array((string) $value, $parameters);
- }
- /**
- * Validate an attribute is not contained within a list of values.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateNotIn($attribute, $value, $parameters)
- {
- return ! $this->validateIn($attribute, $value, $parameters);
- }
- /**
- * Validate an attribute is unique among other values.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateDistinct($attribute, $value, $parameters)
- {
- $attributeName = $this->getPrimaryAttribute($attribute);
- $explicitPath = $this->getLeadingExplicitAttributePath($attributeName);
- $attributeData = $this->extractDataFromPath($explicitPath);
- $data = Arr::where(Arr::dot($attributeData), function ($key) use ($attribute, $attributeName) {
- return $key != $attribute && Str::is($attributeName, $key);
- });
- return ! in_array($value, array_values($data));
- }
- /**
- * Validate the uniqueness of an attribute value on a given database table.
- *
- * If a database column is not specified, the attribute will be used.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateUnique($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'unique');
- list($connection, $table) = $this->parseTable($parameters[0]);
- // The second parameter position holds the name of the column that needs to
- // be verified as unique. If this parameter isn't specified we will just
- // assume that this column to be verified shares the attribute's name.
- $column = isset($parameters[1])
- ? $parameters[1] : $this->guessColumnForQuery($attribute);
- list($idColumn, $id) = [null, null];
- if (isset($parameters[2])) {
- list($idColumn, $id) = $this->getUniqueIds($parameters);
- if (preg_match('/\[(.*)\]/', $id, $matches)) {
- $id = $this->getValue($matches[1]);
- }
- if (strtolower($id) == 'null') {
- $id = null;
- }
- }
- // The presence verifier is responsible for counting rows within this store
- // mechanism which might be a relational database or any other permanent
- // data store like Redis, etc. We will use it to determine uniqueness.
- $verifier = $this->getPresenceVerifier();
- $verifier->setConnection($connection);
- $extra = $this->getUniqueExtra($parameters);
- return $verifier->getCount(
- $table, $column, $value, $id, $idColumn, $extra
- ) == 0;
- }
- /**
- * Parse the connection / table for the unique / exists rules.
- *
- * @param string $table
- * @return array
- */
- protected function parseTable($table)
- {
- return Str::contains($table, '.') ? explode('.', $table, 2) : [null, $table];
- }
- /**
- * Get the excluded ID column and value for the unique rule.
- *
- * @param array $parameters
- * @return array
- */
- protected function getUniqueIds($parameters)
- {
- $idColumn = isset($parameters[3]) ? $parameters[3] : 'id';
- return [$idColumn, $parameters[2]];
- }
- /**
- * Get the extra conditions for a unique rule.
- *
- * @param array $parameters
- * @return array
- */
- protected function getUniqueExtra($parameters)
- {
- if (isset($parameters[4])) {
- return $this->getExtraConditions(array_slice($parameters, 4));
- }
- return [];
- }
- /**
- * Validate the existence of an attribute value in a database table.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateExists($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'exists');
- list($connection, $table) = $this->parseTable($parameters[0]);
- // The second parameter position holds the name of the column that should be
- // verified as existing. If this parameter is not specified we will guess
- // that the columns being "verified" shares the given attribute's name.
- $column = isset($parameters[1])
- ? $parameters[1] : $this->guessColumnForQuery($attribute);
- $expected = (is_array($value)) ? count($value) : 1;
- return $this->getExistCount(
- $connection, $table, $column, $value, $parameters
- ) >= $expected;
- }
- /**
- * Get the number of records that exist in storage.
- *
- * @param mixed $connection
- * @param string $table
- * @param string $column
- * @param mixed $value
- * @param array $parameters
- * @return int
- */
- protected function getExistCount($connection, $table, $column, $value, $parameters)
- {
- $verifier = $this->getPresenceVerifier();
- $verifier->setConnection($connection);
- $extra = $this->getExtraExistConditions($parameters);
- if (is_array($value)) {
- return $verifier->getMultiCount($table, $column, $value, $extra);
- }
- return $verifier->getCount($table, $column, $value, null, null, $extra);
- }
- /**
- * Get the extra exist conditions.
- *
- * @param array $parameters
- * @return array
- */
- protected function getExtraExistConditions(array $parameters)
- {
- return $this->getExtraConditions(array_values(array_slice($parameters, 2)));
- }
- /**
- * Get the extra conditions for a unique / exists rule.
- *
- * @param array $segments
- * @return array
- */
- protected function getExtraConditions(array $segments)
- {
- $extra = [];
- $count = count($segments);
- for ($i = 0; $i < $count; $i = $i + 2) {
- $extra[$segments[$i]] = $segments[$i + 1];
- }
- return $extra;
- }
- /**
- * Guess the database column from the given attribute name.
- *
- * @param string $attribute
- * @return string
- */
- public function guessColumnForQuery($attribute)
- {
- if (in_array($attribute, array_collapse($this->implicitAttributes))
- && ! is_numeric($last = last(explode('.', $attribute)))) {
- return $last;
- }
- return $attribute;
- }
- /**
- * Validate that an attribute is a valid IP.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateIp($attribute, $value)
- {
- return filter_var($value, FILTER_VALIDATE_IP) !== false;
- }
- /**
- * Validate that an attribute is a valid e-mail address.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateEmail($attribute, $value)
- {
- return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
- }
- /**
- * Validate that an attribute is a valid URL.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateUrl($attribute, $value)
- {
- /*
- * This pattern is derived from Symfony\Component\Validator\Constraints\UrlValidator (2.7.4).
- *
- * (c) Fabien Potencier <fabien@symfony.com> http://symfony.com
- */
- $pattern = '~^
- ((aaa|aaas|about|acap|acct|acr|adiumxtra|afp|afs|aim|apt|attachment|aw|barion|beshare|bitcoin|blob|bolo|callto|cap|chrome|chrome-extension|cid|coap|coaps|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-playcontainer|dlna-playsingle|dns|dntp|dtn|dvb|ed2k|example|facetime|fax|feed|feedready|file|filesystem|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|ham|hcp|http|https|iax|icap|icon|im|imap|info|iotdisco|ipn|ipp|ipps|irc|irc6|ircs|iris|iris.beep|iris.lwz|iris.xpc|iris.xpcs|itms|jabber|jar|jms|keyparc|lastfm|ldap|ldaps|magnet|mailserver|mailto|maps|market|message|mid|mms|modem|ms-help|ms-settings|ms-settings-airplanemode|ms-settings-bluetooth|ms-settings-camera|ms-settings-cellular|ms-settings-cloudstorage|ms-settings-emailandaccounts|ms-settings-language|ms-settings-location|ms-settings-lock|ms-settings-nfctransactions|ms-settings-notifications|ms-settings-power|ms-settings-privacy|ms-settings-proximity|ms-settings-screenrotation|ms-settings-wifi|ms-settings-workplace|msnim|msrp|msrps|mtqp|mumble|mupdate|mvn|news|nfs|ni|nih|nntp|notes|oid|opaquelocktoken|pack|palm|paparazzi|pkcs11|platform|pop|pres|prospero|proxy|psyc|query|redis|rediss|reload|res|resource|rmi|rsync|rtmfp|rtmp|rtsp|rtsps|rtspu|secondlife|service|session|sftp|sgn|shttp|sieve|sip|sips|skype|smb|sms|smtp|snews|snmp|soap.beep|soap.beeps|soldat|spotify|ssh|steam|stun|stuns|submit|svn|tag|teamspeak|tel|teliaeid|telnet|tftp|things|thismessage|tip|tn3270|turn|turns|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|videotex|view-source|wais|webcal|ws|wss|wtai|wyciwyg|xcon|xcon-userid|xfire|xmlrpc\.beep|xmlrpc.beeps|xmpp|xri|ymsgr|z39\.50|z39\.50r|z39\.50s)):// # protocol
- (([\pL\pN-]+:)?([\pL\pN-]+)@)? # basic auth
- (
- ([\pL\pN\pS-\.])+(\.?([\pL]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
- | # or
- \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # a IP address
- | # or
- \[
- (?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::))))
- \] # a IPv6 address
- )
- (:[0-9]+)? # a port (optional)
- (/?|/\S+) # a /, nothing or a / with something
- $~ixu';
- return preg_match($pattern, $value) === 1;
- }
- /**
- * Validate that an attribute is an active URL.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateActiveUrl($attribute, $value)
- {
- if (! is_string($value)) {
- return false;
- }
- if ($url = parse_url($value, PHP_URL_HOST)) {
- return count(dns_get_record($url, DNS_A | DNS_AAAA)) > 0;
- }
- return false;
- }
- /**
- * Validate the MIME type of a file is an image MIME type.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateImage($attribute, $value)
- {
- return $this->validateMimes($attribute, $value, ['jpeg', 'png', 'gif', 'bmp', 'svg']);
- }
- /**
- * Validate the guessed extension of a file upload is in a set of file extensions.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateMimes($attribute, $value, $parameters)
- {
- if (! $this->isAValidFileInstance($value)) {
- return false;
- }
- return $value->getPath() != '' && in_array($value->guessExtension(), $parameters);
- }
- /**
- * Validate the MIME type of a file upload attribute is in a set of MIME types.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateMimetypes($attribute, $value, $parameters)
- {
- if (! $this->isAValidFileInstance($value)) {
- return false;
- }
- return $value->getPath() != '' && in_array($value->getMimeType(), $parameters);
- }
- /**
- * Check that the given value is a valid file instance.
- *
- * @param mixed $value
- * @return bool
- */
- public function isAValidFileInstance($value)
- {
- if ($value instanceof UploadedFile && ! $value->isValid()) {
- return false;
- }
- return $value instanceof File;
- }
- /**
- * Validate that an attribute contains only alphabetic characters.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateAlpha($attribute, $value)
- {
- return is_string($value) && preg_match('/^[\pL\pM]+$/u', $value);
- }
- /**
- * Validate that an attribute contains only alpha-numeric characters.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateAlphaNum($attribute, $value)
- {
- if (! is_string($value) && ! is_numeric($value)) {
- return false;
- }
- return preg_match('/^[\pL\pM\pN]+$/u', $value);
- }
- /**
- * Validate that an attribute contains only alpha-numeric characters, dashes, and underscores.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateAlphaDash($attribute, $value)
- {
- if (! is_string($value) && ! is_numeric($value)) {
- return false;
- }
- return preg_match('/^[\pL\pM\pN_-]+$/u', $value);
- }
- /**
- * Validate that an attribute passes a regular expression check.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateRegex($attribute, $value, $parameters)
- {
- if (! is_string($value) && ! is_numeric($value)) {
- return false;
- }
- $this->requireParameterCount(1, $parameters, 'regex');
- return preg_match($parameters[0], $value);
- }
- /**
- * Validate that an attribute is a valid date.
- *
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- protected function validateDate($attribute, $value)
- {
- if ($value instanceof DateTime) {
- return true;
- }
- if (! is_string($value) || strtotime($value) === false) {
- return false;
- }
- $date = date_parse($value);
- return checkdate($date['month'], $date['day'], $date['year']);
- }
- /**
- * Validate that an attribute matches a date format.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateDateFormat($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'date_format');
- if (! is_string($value)) {
- return false;
- }
- $parsed = date_parse_from_format($parameters[0], $value);
- return $parsed['error_count'] === 0 && $parsed['warning_count'] === 0;
- }
- /**
- * Validate the date is before a given date.
- *
- * @param string $attribute
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateBefore($attribute, $value, $parameters)
- {
- $this->requireParameterCount(1, $parameters, 'before');
- if (! is_string($value)) {
- return false;
- }
- if ($format = $this->getDateFormat($attribute)) {
- return $this->validateBeforeWithFormat($format, $value, $parameters);
- }
- if (! ($date = strtotime($parameters[0]))) {
- return strtotime($value) < strtotime($this->getValue($parameters[0]));
- }
- return strtotime($value) < $date;
- }
- /**
- * Validate the date is before a given date with a given format.
- *
- * @param string $format
- * @param mixed $value
- * @param array $parameters
- * @return bool
- */
- protected function validateBeforeWithFormat($format, $value, $parameters)
- {
- $param = $this->getValue(