This commit is contained in:
TiclemFR
2023-12-29 16:00:02 +01:00
parent 9d79d7c0c6
commit 884eb3011a
8361 changed files with 1160554 additions and 4 deletions

View File

@@ -0,0 +1,94 @@
<?php
namespace Illuminate\Validation;
use Illuminate\Contracts\Validation\Rule as RuleContract;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Translation\CreatesPotentiallyTranslatedStrings;
class ClosureValidationRule implements RuleContract, ValidatorAwareRule
{
use CreatesPotentiallyTranslatedStrings;
/**
* The callback that validates the attribute.
*
* @var \Closure
*/
public $callback;
/**
* Indicates if the validation callback failed.
*
* @var bool
*/
public $failed = false;
/**
* The validation error messages.
*
* @var array
*/
public $messages = [];
/**
* The current validator.
*
* @var \Illuminate\Validation\Validator
*/
protected $validator;
/**
* Create a new Closure based validation rule.
*
* @param \Closure $callback
* @return void
*/
public function __construct($callback)
{
$this->callback = $callback;
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$this->failed = false;
$this->callback->__invoke($attribute, $value, function ($attribute, $message = null) {
$this->failed = true;
return $this->pendingPotentiallyTranslatedString($attribute, $message);
}, $this->validator);
return ! $this->failed;
}
/**
* Get the validation error messages.
*
* @return string
*/
public function message()
{
return $this->messages;
}
/**
* Set the current validator.
*
* @param \Illuminate\Validation\Validator $validator
* @return $this
*/
public function setValidator($validator)
{
$this->validator = $validator;
return $this;
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Illuminate\Validation\Concerns;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Validation\EmailValidation;
class FilterEmailValidation implements EmailValidation
{
/**
* The flags to pass to the filter_var function.
*
* @var int|null
*/
protected $flags;
/**
* Create a new validation instance.
*
* @param int $flags
* @return void
*/
public function __construct($flags = null)
{
$this->flags = $flags;
}
/**
* Create a new instance which allows any unicode characters in local-part.
*
* @return static
*/
public static function unicode()
{
return new static(FILTER_FLAG_EMAIL_UNICODE);
}
/**
* Returns true if the given email is valid.
*
* @param string $email
* @param \Egulias\EmailValidator\EmailLexer $emailLexer
* @return bool
*/
public function isValid(string $email, EmailLexer $emailLexer): bool
{
return is_null($this->flags)
? filter_var($email, FILTER_VALIDATE_EMAIL) !== false
: filter_var($email, FILTER_VALIDATE_EMAIL, $this->flags) !== false;
}
/**
* Returns the validation error.
*
* @return \Egulias\EmailValidator\Result\InvalidEmail|null
*/
public function getError(): ?InvalidEmail
{
return null;
}
/**
* Returns the validation warnings.
*
* @return \Egulias\EmailValidator\Warning\Warning[]
*/
public function getWarnings(): array
{
return [];
}
}

View File

@@ -0,0 +1,524 @@
<?php
namespace Illuminate\Validation\Concerns;
use Closure;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\File\UploadedFile;
trait FormatsMessages
{
use ReplacesAttributes;
/**
* Get the validation message for an attribute and rule.
*
* @param string $attribute
* @param string $rule
* @return string
*/
protected function getMessage($attribute, $rule)
{
$attributeWithPlaceholders = $attribute;
$attribute = $this->replacePlaceholderInString($attribute);
$inlineMessage = $this->getInlineMessage($attribute, $rule);
// First we will retrieve the custom message for the validation rule if one
// exists. If a custom validation message is being used we'll return the
// custom message, otherwise we'll keep searching for a valid message.
if (! is_null($inlineMessage)) {
return $inlineMessage;
}
$lowerRule = Str::snake($rule);
$customKey = "validation.custom.{$attribute}.{$lowerRule}";
$customMessage = $this->getCustomMessageFromTranslator(
in_array($rule, $this->sizeRules)
? [$customKey.".{$this->getAttributeType($attribute)}", $customKey]
: $customKey
);
// First we check for a custom defined validation message for the attribute
// and rule. This allows the developer to specify specific messages for
// only some attributes and rules that need to get specially formed.
if ($customMessage !== $customKey) {
return $customMessage;
}
// If the rule being validated is a "size" rule, we will need to gather the
// specific error message for the type of attribute being validated such
// as a number, file or string which all have different message types.
elseif (in_array($rule, $this->sizeRules)) {
return $this->getSizeMessage($attributeWithPlaceholders, $rule);
}
// Finally, if no developer specified messages have been set, and no other
// special messages apply for this rule, we will just pull the default
// messages out of the translator service for this validation rule.
$key = "validation.{$lowerRule}";
if ($key !== ($value = $this->translator->get($key))) {
return $value;
}
return $this->getFromLocalArray(
$attribute, $lowerRule, $this->fallbackMessages
) ?: $key;
}
/**
* Get the proper inline error message for standard and size rules.
*
* @param string $attribute
* @param string $rule
* @return string|null
*/
protected function getInlineMessage($attribute, $rule)
{
$inlineEntry = $this->getFromLocalArray($attribute, Str::snake($rule));
return is_array($inlineEntry) && in_array($rule, $this->sizeRules)
? $inlineEntry[$this->getAttributeType($attribute)]
: $inlineEntry;
}
/**
* Get the inline message for a rule if it exists.
*
* @param string $attribute
* @param string $lowerRule
* @param array|null $source
* @return string|null
*/
protected function getFromLocalArray($attribute, $lowerRule, $source = null)
{
$source = $source ?: $this->customMessages;
$keys = ["{$attribute}.{$lowerRule}", $lowerRule, $attribute];
// First we will check for a custom message for an attribute specific rule
// message for the fields, then we will check for a general custom line
// that is not attribute specific. If we find either we'll return it.
foreach ($keys as $key) {
foreach (array_keys($source) as $sourceKey) {
if (str_contains($sourceKey, '*')) {
$pattern = str_replace('\*', '([^.]*)', preg_quote($sourceKey, '#'));
if (preg_match('#^'.$pattern.'\z#u', $key) === 1) {
$message = $source[$sourceKey];
if (is_array($message) && isset($message[$lowerRule])) {
return $message[$lowerRule];
}
return $message;
}
continue;
}
if (Str::is($sourceKey, $key)) {
$message = $source[$sourceKey];
if ($sourceKey === $attribute && is_array($message) && isset($message[$lowerRule])) {
return $message[$lowerRule];
}
return $message;
}
}
}
}
/**
* Get the custom error message from the translator.
*
* @param array|string $keys
* @return string
*/
protected function getCustomMessageFromTranslator($keys)
{
foreach (Arr::wrap($keys) as $key) {
if (($message = $this->translator->get($key)) !== $key) {
return $message;
}
// If an exact match was not found for the key, we will collapse all of these
// messages and loop through them and try to find a wildcard match for the
// given key. Otherwise, we will simply return the key's value back out.
$shortKey = preg_replace(
'/^validation\.custom\./', '', $key
);
$message = $this->getWildcardCustomMessages(Arr::dot(
(array) $this->translator->get('validation.custom')
), $shortKey, $key);
if ($message !== $key) {
return $message;
}
}
return Arr::last(Arr::wrap($keys));
}
/**
* Check the given messages for a wildcard key.
*
* @param array $messages
* @param string $search
* @param string $default
* @return string
*/
protected function getWildcardCustomMessages($messages, $search, $default)
{
foreach ($messages as $key => $message) {
if ($search === $key || (Str::contains($key, ['*']) && Str::is($key, $search))) {
return $message;
}
}
return $default;
}
/**
* Get the proper error message for an attribute and size rule.
*
* @param string $attribute
* @param string $rule
* @return string
*/
protected function getSizeMessage($attribute, $rule)
{
$lowerRule = Str::snake($rule);
// There are three different types of size validations. The attribute may be
// either a number, file, or string so we will check a few things to know
// which type of value it is and return the correct line for that type.
$type = $this->getAttributeType($attribute);
$key = "validation.{$lowerRule}.{$type}";
return $this->translator->get($key);
}
/**
* Get the data type of the given attribute.
*
* @param string $attribute
* @return string
*/
protected function getAttributeType($attribute)
{
// We assume that the attributes present in the file array are files so that
// means that if the attribute does not have a numeric rule and the files
// list doesn't have it we'll just consider it a string by elimination.
if ($this->hasRule($attribute, $this->numericRules)) {
return 'numeric';
} elseif ($this->hasRule($attribute, ['Array'])) {
return 'array';
} elseif ($this->getValue($attribute) instanceof UploadedFile) {
return 'file';
}
return 'string';
}
/**
* Replace all error message place-holders with actual values.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/
public function makeReplacements($message, $attribute, $rule, $parameters)
{
$message = $this->replaceAttributePlaceholder(
$message, $this->getDisplayableAttribute($attribute)
);
$message = $this->replaceInputPlaceholder($message, $attribute);
$message = $this->replaceIndexPlaceholder($message, $attribute);
$message = $this->replacePositionPlaceholder($message, $attribute);
if (isset($this->replacers[Str::snake($rule)])) {
return $this->callReplacer($message, $attribute, Str::snake($rule), $parameters, $this);
} elseif (method_exists($this, $replacer = "replace{$rule}")) {
return $this->$replacer($message, $attribute, $rule, $parameters);
}
return $message;
}
/**
* Get the displayable name of the attribute.
*
* @param string $attribute
* @return string
*/
public function getDisplayableAttribute($attribute)
{
$primaryAttribute = $this->getPrimaryAttribute($attribute);
$expectedAttributes = $attribute != $primaryAttribute
? [$attribute, $primaryAttribute] : [$attribute];
foreach ($expectedAttributes as $name) {
// The developer may dynamically specify the array of custom attributes on this
// validator instance. If the attribute exists in this array it is used over
// the other ways of pulling the attribute name for this given attributes.
if (isset($this->customAttributes[$name])) {
return $this->customAttributes[$name];
}
// We allow for a developer to specify language lines for any attribute in this
// application, which allows flexibility for displaying a unique displayable
// version of the attribute name instead of the name used in an HTTP POST.
if ($line = $this->getAttributeFromTranslations($name)) {
return $line;
}
}
// When no language line has been specified for the attribute and it is also
// an implicit attribute we will display the raw attribute's name and not
// modify it with any of these replacements before we display the name.
if (isset($this->implicitAttributes[$primaryAttribute])) {
return ($formatter = $this->implicitAttributesFormatter)
? $formatter($attribute)
: $attribute;
}
return str_replace('_', ' ', Str::snake($attribute));
}
/**
* Get the given attribute from the attribute translations.
*
* @param string $name
* @return string
*/
protected function getAttributeFromTranslations($name)
{
return Arr::get($this->translator->get('validation.attributes'), $name);
}
/**
* Replace the :attribute placeholder in the given message.
*
* @param string $message
* @param string $value
* @return string
*/
protected function replaceAttributePlaceholder($message, $value)
{
return str_replace(
[':attribute', ':ATTRIBUTE', ':Attribute'],
[$value, Str::upper($value), Str::ucfirst($value)],
$message
);
}
/**
* Replace the :index placeholder in the given message.
*
* @param string $message
* @param string $attribute
* @return string
*/
protected function replaceIndexPlaceholder($message, $attribute)
{
return $this->replaceIndexOrPositionPlaceholder(
$message, $attribute, 'index'
);
}
/**
* Replace the :position placeholder in the given message.
*
* @param string $message
* @param string $attribute
* @return string
*/
protected function replacePositionPlaceholder($message, $attribute)
{
return $this->replaceIndexOrPositionPlaceholder(
$message, $attribute, 'position', fn ($segment) => $segment + 1
);
}
/**
* Replace the :index or :position placeholder in the given message.
*
* @param string $message
* @param string $attribute
* @param string $placeholder
* @param \Closure|null $modifier
* @return string
*/
protected function replaceIndexOrPositionPlaceholder($message, $attribute, $placeholder, Closure $modifier = null)
{
$segments = explode('.', $attribute);
$modifier ??= fn ($value) => $value;
$numericIndex = 1;
foreach ($segments as $segment) {
if (is_numeric($segment)) {
if ($numericIndex === 1) {
$message = str_ireplace(':'.$placeholder, $modifier((int) $segment), $message);
}
$message = str_ireplace(
':'.$this->numberToIndexOrPositionWord($numericIndex).'-'.$placeholder,
$modifier((int) $segment),
$message
);
$numericIndex++;
}
}
return $message;
}
/**
* Get the word for a index or position segment.
*
* @param int $value
* @return string
*/
protected function numberToIndexOrPositionWord(int $value)
{
return [
1 => 'first',
2 => 'second',
3 => 'third',
4 => 'fourth',
5 => 'fifth',
6 => 'sixth',
7 => 'seventh',
8 => 'eighth',
9 => 'ninth',
10 => 'tenth',
][(int) $value] ?? 'other';
}
/**
* Replace the :input placeholder in the given message.
*
* @param string $message
* @param string $attribute
* @return string
*/
protected function replaceInputPlaceholder($message, $attribute)
{
$actualValue = $this->getValue($attribute);
if (is_scalar($actualValue) || is_null($actualValue)) {
$message = str_replace(':input', $this->getDisplayableValue($attribute, $actualValue), $message);
}
return $message;
}
/**
* Get the displayable name of the value.
*
* @param string $attribute
* @param mixed $value
* @return string
*/
public function getDisplayableValue($attribute, $value)
{
if (isset($this->customValues[$attribute][$value])) {
return $this->customValues[$attribute][$value];
}
if (is_array($value)) {
return 'array';
}
$key = "validation.values.{$attribute}.{$value}";
if (($line = $this->translator->get($key)) !== $key) {
return $line;
}
if (is_bool($value)) {
return $value ? 'true' : 'false';
}
if (is_null($value)) {
return 'empty';
}
return (string) $value;
}
/**
* Transform an array of attributes to their displayable form.
*
* @param array $values
* @return array
*/
protected function getAttributeList(array $values)
{
$attributes = [];
// For each attribute in the list we will simply get its displayable form as
// this is convenient when replacing lists of parameters like some of the
// replacement functions do when formatting out the validation message.
foreach ($values as $key => $value) {
$attributes[$key] = $this->getDisplayableAttribute($value);
}
return $attributes;
}
/**
* Call a custom validator message replacer.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @param \Illuminate\Validation\Validator $validator
* @return string|null
*/
protected function callReplacer($message, $attribute, $rule, $parameters, $validator)
{
$callback = $this->replacers[$rule];
if ($callback instanceof Closure) {
return $callback(...func_get_args());
} elseif (is_string($callback)) {
return $this->callClassBasedReplacer($callback, $message, $attribute, $rule, $parameters, $validator);
}
}
/**
* Call a class based validator message replacer.
*
* @param string $callback
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @param \Illuminate\Validation\Validator $validator
* @return string
*/
protected function callClassBasedReplacer($callback, $message, $attribute, $rule, $parameters, $validator)
{
[$class, $method] = Str::parseCallback($callback, 'replace');
return $this->container->make($class)->{$method}(...array_slice(func_get_args(), 1));
}
}

View File

@@ -0,0 +1,869 @@
<?php
namespace Illuminate\Validation\Concerns;
use Illuminate\Support\Arr;
trait ReplacesAttributes
{
/**
* Replace all place-holders for the accepted_if rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceAcceptedIf($message, $attribute, $rule, $parameters)
{
$parameters[1] = $this->getDisplayableValue($parameters[0], Arr::get($this->data, $parameters[0]));
$parameters[0] = $this->getDisplayableAttribute($parameters[0]);
return str_replace([':other', ':value'], $parameters, $message);
}
/**
* Replace all place-holders for the declined_if rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceDeclinedIf($message, $attribute, $rule, $parameters)
{
$parameters[1] = $this->getDisplayableValue($parameters[0], Arr::get($this->data, $parameters[0]));
$parameters[0] = $this->getDisplayableAttribute($parameters[0]);
return str_replace([':other', ':value'], $parameters, $message);
}
/**
* Replace all place-holders for the between rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceBetween($message, $attribute, $rule, $parameters)
{
return str_replace([':min', ':max'], $parameters, $message);
}
/**
* Replace all place-holders for the date_format rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceDateFormat($message, $attribute, $rule, $parameters)
{
return str_replace(':format', $parameters[0], $message);
}
/**
* Replace all place-holders for the decimal rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,int> $parameters
* @return string
*/
protected function replaceDecimal($message, $attribute, $rule, $parameters)
{
return str_replace(
':decimal',
isset($parameters[1])
? $parameters[0].'-'.$parameters[1]
: $parameters[0],
$message
);
}
/**
* Replace all place-holders for the different rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceDifferent($message, $attribute, $rule, $parameters)
{
return $this->replaceSame($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the digits rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceDigits($message, $attribute, $rule, $parameters)
{
return str_replace(':digits', $parameters[0], $message);
}
/**
* Replace all place-holders for the digits (between) rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceDigitsBetween($message, $attribute, $rule, $parameters)
{
return $this->replaceBetween($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the extensions rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceExtensions($message, $attribute, $rule, $parameters)
{
return str_replace(':values', implode(', ', $parameters), $message);
}
/**
* Replace all place-holders for the min rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMin($message, $attribute, $rule, $parameters)
{
return str_replace(':min', $parameters[0], $message);
}
/**
* Replace all place-holders for the min digits rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMinDigits($message, $attribute, $rule, $parameters)
{
return str_replace(':min', $parameters[0], $message);
}
/**
* Replace all place-holders for the max rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMax($message, $attribute, $rule, $parameters)
{
return str_replace(':max', $parameters[0], $message);
}
/**
* Replace all place-holders for the max digits rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMaxDigits($message, $attribute, $rule, $parameters)
{
return str_replace(':max', $parameters[0], $message);
}
/**
* Replace all place-holders for the missing_if rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMissingIf($message, $attribute, $rule, $parameters)
{
$parameters[1] = $this->getDisplayableValue($parameters[0], Arr::get($this->data, $parameters[0]));
$parameters[0] = $this->getDisplayableAttribute($parameters[0]);
return str_replace([':other', ':value'], $parameters, $message);
}
/**
* Replace all place-holders for the missing_unless rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMissingUnless($message, $attribute, $rule, $parameters)
{
return str_replace([':other', ':value'], [
$this->getDisplayableAttribute($parameters[0]),
$this->getDisplayableValue($parameters[0], $parameters[1]),
], $message);
}
/**
* Replace all place-holders for the missing_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMissingWith($message, $attribute, $rule, $parameters)
{
return str_replace(':values', implode(' / ', $this->getAttributeList($parameters)), $message);
}
/**
* Replace all place-holders for the missing_with_all rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMissingWithAll($message, $attribute, $rule, $parameters)
{
return $this->replaceMissingWith($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the multiple_of rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMultipleOf($message, $attribute, $rule, $parameters)
{
return str_replace(':value', $parameters[0] ?? '', $message);
}
/**
* Replace all place-holders for the in rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceIn($message, $attribute, $rule, $parameters)
{
foreach ($parameters as &$parameter) {
$parameter = $this->getDisplayableValue($attribute, $parameter);
}
return str_replace(':values', implode(', ', $parameters), $message);
}
/**
* Replace all place-holders for the not_in rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceNotIn($message, $attribute, $rule, $parameters)
{
return $this->replaceIn($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the in_array rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceInArray($message, $attribute, $rule, $parameters)
{
return str_replace(':other', $this->getDisplayableAttribute($parameters[0]), $message);
}
/**
* Replace all place-holders for the required_array_keys rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceRequiredArrayKeys($message, $attribute, $rule, $parameters)
{
foreach ($parameters as &$parameter) {
$parameter = $this->getDisplayableValue($attribute, $parameter);
}
return str_replace(':values', implode(', ', $parameters), $message);
}
/**
* Replace all place-holders for the mimetypes rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMimetypes($message, $attribute, $rule, $parameters)
{
return str_replace(':values', implode(', ', $parameters), $message);
}
/**
* Replace all place-holders for the mimes rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceMimes($message, $attribute, $rule, $parameters)
{
return str_replace(':values', implode(', ', $parameters), $message);
}
/**
* Replace all place-holders for the present_if rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replacePresentIf($message, $attribute, $rule, $parameters)
{
$parameters[1] = $this->getDisplayableValue($parameters[0], Arr::get($this->data, $parameters[0]));
$parameters[0] = $this->getDisplayableAttribute($parameters[0]);
return str_replace([':other', ':value'], $parameters, $message);
}
/**
* Replace all place-holders for the present_unless rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replacePresentUnless($message, $attribute, $rule, $parameters)
{
return str_replace([':other', ':value'], [
$this->getDisplayableAttribute($parameters[0]),
$this->getDisplayableValue($parameters[0], $parameters[1]),
], $message);
}
/**
* Replace all place-holders for the present_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replacePresentWith($message, $attribute, $rule, $parameters)
{
return str_replace(':values', implode(' / ', $this->getAttributeList($parameters)), $message);
}
/**
* Replace all place-holders for the present_with_all rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replacePresentWithAll($message, $attribute, $rule, $parameters)
{
return $this->replacePresentWith($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the required_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceRequiredWith($message, $attribute, $rule, $parameters)
{
return str_replace(':values', implode(' / ', $this->getAttributeList($parameters)), $message);
}
/**
* Replace all place-holders for the required_with_all rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceRequiredWithAll($message, $attribute, $rule, $parameters)
{
return $this->replaceRequiredWith($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the required_without rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceRequiredWithout($message, $attribute, $rule, $parameters)
{
return $this->replaceRequiredWith($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the required_without_all rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceRequiredWithoutAll($message, $attribute, $rule, $parameters)
{
return $this->replaceRequiredWith($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the size rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceSize($message, $attribute, $rule, $parameters)
{
return str_replace(':size', $parameters[0], $message);
}
/**
* Replace all place-holders for the gt rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceGt($message, $attribute, $rule, $parameters)
{
if (is_null($value = $this->getValue($parameters[0]))) {
return str_replace(':value', $this->getDisplayableAttribute($parameters[0]), $message);
}
return str_replace(':value', $this->getSize($attribute, $value), $message);
}
/**
* Replace all place-holders for the lt rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceLt($message, $attribute, $rule, $parameters)
{
if (is_null($value = $this->getValue($parameters[0]))) {
return str_replace(':value', $this->getDisplayableAttribute($parameters[0]), $message);
}
return str_replace(':value', $this->getSize($attribute, $value), $message);
}
/**
* Replace all place-holders for the gte rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceGte($message, $attribute, $rule, $parameters)
{
if (is_null($value = $this->getValue($parameters[0]))) {
return str_replace(':value', $this->getDisplayableAttribute($parameters[0]), $message);
}
return str_replace(':value', $this->getSize($attribute, $value), $message);
}
/**
* Replace all place-holders for the lte rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceLte($message, $attribute, $rule, $parameters)
{
if (is_null($value = $this->getValue($parameters[0]))) {
return str_replace(':value', $this->getDisplayableAttribute($parameters[0]), $message);
}
return str_replace(':value', $this->getSize($attribute, $value), $message);
}
/**
* Replace all place-holders for the required_if rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceRequiredIf($message, $attribute, $rule, $parameters)
{
$parameters[1] = $this->getDisplayableValue($parameters[0], Arr::get($this->data, $parameters[0]));
$parameters[0] = $this->getDisplayableAttribute($parameters[0]);
return str_replace([':other', ':value'], $parameters, $message);
}
/**
* Replace all place-holders for the required_if_accepted rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceRequiredIfAccepted($message, $attribute, $rule, $parameters)
{
$parameters[0] = $this->getDisplayableAttribute($parameters[0]);
return str_replace([':other'], $parameters, $message);
}
/**
* Replace all place-holders for the required_unless rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceRequiredUnless($message, $attribute, $rule, $parameters)
{
$other = $this->getDisplayableAttribute($parameters[0]);
$values = [];
foreach (array_slice($parameters, 1) as $value) {
$values[] = $this->getDisplayableValue($parameters[0], $value);
}
return str_replace([':other', ':values'], [$other, implode(', ', $values)], $message);
}
/**
* Replace all place-holders for the prohibited_if rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceProhibitedIf($message, $attribute, $rule, $parameters)
{
$parameters[1] = $this->getDisplayableValue($parameters[0], Arr::get($this->data, $parameters[0]));
$parameters[0] = $this->getDisplayableAttribute($parameters[0]);
return str_replace([':other', ':value'], $parameters, $message);
}
/**
* Replace all place-holders for the prohibited_unless rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceProhibitedUnless($message, $attribute, $rule, $parameters)
{
$other = $this->getDisplayableAttribute($parameters[0]);
$values = [];
foreach (array_slice($parameters, 1) as $value) {
$values[] = $this->getDisplayableValue($parameters[0], $value);
}
return str_replace([':other', ':values'], [$other, implode(', ', $values)], $message);
}
/**
* Replace all place-holders for the prohibited_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceProhibits($message, $attribute, $rule, $parameters)
{
return str_replace(':other', implode(' / ', $this->getAttributeList($parameters)), $message);
}
/**
* Replace all place-holders for the same rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceSame($message, $attribute, $rule, $parameters)
{
return str_replace(':other', $this->getDisplayableAttribute($parameters[0]), $message);
}
/**
* Replace all place-holders for the before rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceBefore($message, $attribute, $rule, $parameters)
{
if (! strtotime($parameters[0])) {
return str_replace(':date', $this->getDisplayableAttribute($parameters[0]), $message);
}
return str_replace(':date', $this->getDisplayableValue($attribute, $parameters[0]), $message);
}
/**
* Replace all place-holders for the before_or_equal rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceBeforeOrEqual($message, $attribute, $rule, $parameters)
{
return $this->replaceBefore($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the after rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceAfter($message, $attribute, $rule, $parameters)
{
return $this->replaceBefore($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the after_or_equal rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceAfterOrEqual($message, $attribute, $rule, $parameters)
{
return $this->replaceBefore($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the date_equals rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceDateEquals($message, $attribute, $rule, $parameters)
{
return $this->replaceBefore($message, $attribute, $rule, $parameters);
}
/**
* Replace all place-holders for the dimensions rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceDimensions($message, $attribute, $rule, $parameters)
{
$parameters = $this->parseNamedParameters($parameters);
if (is_array($parameters)) {
foreach ($parameters as $key => $value) {
$message = str_replace(':'.$key, $value, $message);
}
}
return $message;
}
/**
* Replace all place-holders for the ends_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceEndsWith($message, $attribute, $rule, $parameters)
{
foreach ($parameters as &$parameter) {
$parameter = $this->getDisplayableValue($attribute, $parameter);
}
return str_replace(':values', implode(', ', $parameters), $message);
}
/**
* Replace all place-holders for the doesnt_end_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceDoesntEndWith($message, $attribute, $rule, $parameters)
{
foreach ($parameters as &$parameter) {
$parameter = $this->getDisplayableValue($attribute, $parameter);
}
return str_replace(':values', implode(', ', $parameters), $message);
}
/**
* Replace all place-holders for the starts_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceStartsWith($message, $attribute, $rule, $parameters)
{
foreach ($parameters as &$parameter) {
$parameter = $this->getDisplayableValue($attribute, $parameter);
}
return str_replace(':values', implode(', ', $parameters), $message);
}
/**
* Replace all place-holders for the doesnt_start_with rule.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array<int,string> $parameters
* @return string
*/
protected function replaceDoesntStartWith($message, $attribute, $rule, $parameters)
{
foreach ($parameters as &$parameter) {
$parameter = $this->getDisplayableValue($attribute, $parameter);
}
return str_replace(':values', implode(', ', $parameters), $message);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
<?php
namespace Illuminate\Validation;
use Illuminate\Support\Fluent;
class ConditionalRules
{
/**
* The boolean condition indicating if the rules should be added to the attribute.
*
* @var callable|bool
*/
protected $condition;
/**
* The rules to be added to the attribute.
*
* @var array|string|\Closure
*/
protected $rules;
/**
* The rules to be added to the attribute if the condition fails.
*
* @var array|string|\Closure
*/
protected $defaultRules;
/**
* Create a new conditional rules instance.
*
* @param callable|bool $condition
* @param array|string|\Closure $rules
* @param array|string|\Closure $defaultRules
* @return void
*/
public function __construct($condition, $rules, $defaultRules = [])
{
$this->condition = $condition;
$this->rules = $rules;
$this->defaultRules = $defaultRules;
}
/**
* Determine if the conditional rules should be added.
*
* @param array $data
* @return bool
*/
public function passes(array $data = [])
{
return is_callable($this->condition)
? call_user_func($this->condition, new Fluent($data))
: $this->condition;
}
/**
* Get the rules.
*
* @param array $data
* @return array
*/
public function rules(array $data = [])
{
return is_string($this->rules)
? explode('|', $this->rules)
: value($this->rules, new Fluent($data));
}
/**
* Get the default rules.
*
* @param array $data
* @return array
*/
public function defaultRules(array $data = [])
{
return is_string($this->defaultRules)
? explode('|', $this->defaultRules)
: value($this->defaultRules, new Fluent($data));
}
}

View File

@@ -0,0 +1,137 @@
<?php
namespace Illuminate\Validation;
use Closure;
use Illuminate\Database\ConnectionResolverInterface;
class DatabasePresenceVerifier implements DatabasePresenceVerifierInterface
{
/**
* The database connection instance.
*
* @var \Illuminate\Database\ConnectionResolverInterface
*/
protected $db;
/**
* The database connection to use.
*
* @var string
*/
protected $connection;
/**
* Create a new database presence verifier.
*
* @param \Illuminate\Database\ConnectionResolverInterface $db
* @return void
*/
public function __construct(ConnectionResolverInterface $db)
{
$this->db = $db;
}
/**
* Count the number of objects in a collection having the given value.
*
* @param string $collection
* @param string $column
* @param string $value
* @param int|null $excludeId
* @param string|null $idColumn
* @param array $extra
* @return int
*/
public function getCount($collection, $column, $value, $excludeId = null, $idColumn = null, array $extra = [])
{
$query = $this->table($collection)->where($column, '=', $value);
if (! is_null($excludeId) && $excludeId !== 'NULL') {
$query->where($idColumn ?: 'id', '<>', $excludeId);
}
return $this->addConditions($query, $extra)->count();
}
/**
* Count the number of objects in a collection with the given values.
*
* @param string $collection
* @param string $column
* @param array $values
* @param array $extra
* @return int
*/
public function getMultiCount($collection, $column, array $values, array $extra = [])
{
$query = $this->table($collection)->whereIn($column, $values);
return $this->addConditions($query, $extra)->distinct()->count($column);
}
/**
* Add the given conditions to the query.
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $conditions
* @return \Illuminate\Database\Query\Builder
*/
protected function addConditions($query, $conditions)
{
foreach ($conditions as $key => $value) {
if ($value instanceof Closure) {
$query->where(function ($query) use ($value) {
$value($query);
});
} else {
$this->addWhere($query, $key, $value);
}
}
return $query;
}
/**
* Add a "where" clause to the given query.
*
* @param \Illuminate\Database\Query\Builder $query
* @param string $key
* @param string $extraValue
* @return void
*/
protected function addWhere($query, $key, $extraValue)
{
if ($extraValue === 'NULL') {
$query->whereNull($key);
} elseif ($extraValue === 'NOT_NULL') {
$query->whereNotNull($key);
} elseif (str_starts_with($extraValue, '!')) {
$query->where($key, '!=', mb_substr($extraValue, 1));
} else {
$query->where($key, $extraValue);
}
}
/**
* Get a query builder for the given table.
*
* @param string $table
* @return \Illuminate\Database\Query\Builder
*/
protected function table($table)
{
return $this->db->connection($this->connection)->table($table)->useWritePdo();
}
/**
* Set the connection to be used.
*
* @param string $connection
* @return void
*/
public function setConnection($connection)
{
$this->connection = $connection;
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Illuminate\Validation;
interface DatabasePresenceVerifierInterface extends PresenceVerifierInterface
{
/**
* Set the connection to be used.
*
* @param string $connection
* @return void
*/
public function setConnection($connection);
}

View File

@@ -0,0 +1,335 @@
<?php
namespace Illuminate\Validation;
use Closure;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Translation\Translator;
use Illuminate\Contracts\Validation\Factory as FactoryContract;
use Illuminate\Support\Str;
class Factory implements FactoryContract
{
/**
* The Translator implementation.
*
* @var \Illuminate\Contracts\Translation\Translator
*/
protected $translator;
/**
* The Presence Verifier implementation.
*
* @var \Illuminate\Validation\PresenceVerifierInterface
*/
protected $verifier;
/**
* The IoC container instance.
*
* @var \Illuminate\Contracts\Container\Container|null
*/
protected $container;
/**
* All of the custom validator extensions.
*
* @var array<string, \Closure|string>
*/
protected $extensions = [];
/**
* All of the custom implicit validator extensions.
*
* @var array<string, \Closure|string>
*/
protected $implicitExtensions = [];
/**
* All of the custom dependent validator extensions.
*
* @var array<string, \Closure|string>
*/
protected $dependentExtensions = [];
/**
* All of the custom validator message replacers.
*
* @var array<string, \Closure|string>
*/
protected $replacers = [];
/**
* All of the fallback messages for custom rules.
*
* @var array<string, string>
*/
protected $fallbackMessages = [];
/**
* Indicates that unvalidated array keys should be excluded, even if the parent array was validated.
*
* @var bool
*/
protected $excludeUnvalidatedArrayKeys = true;
/**
* The Validator resolver instance.
*
* @var \Closure
*/
protected $resolver;
/**
* Create a new Validator factory instance.
*
* @param \Illuminate\Contracts\Translation\Translator $translator
* @param \Illuminate\Contracts\Container\Container|null $container
* @return void
*/
public function __construct(Translator $translator, Container $container = null)
{
$this->container = $container;
$this->translator = $translator;
}
/**
* Create a new Validator instance.
*
* @param array $data
* @param array $rules
* @param array $messages
* @param array $attributes
* @return \Illuminate\Validation\Validator
*/
public function make(array $data, array $rules, array $messages = [], array $attributes = [])
{
$validator = $this->resolve(
$data, $rules, $messages, $attributes
);
// The presence verifier is responsible for checking the unique and exists data
// for the validator. It is behind an interface so that multiple versions of
// it may be written besides database. We'll inject it into the validator.
if (! is_null($this->verifier)) {
$validator->setPresenceVerifier($this->verifier);
}
// Next we'll set the IoC container instance of the validator, which is used to
// resolve out class based validator extensions. If it is not set then these
// types of extensions will not be possible on these validation instances.
if (! is_null($this->container)) {
$validator->setContainer($this->container);
}
$validator->excludeUnvalidatedArrayKeys = $this->excludeUnvalidatedArrayKeys;
$this->addExtensions($validator);
return $validator;
}
/**
* Validate the given data against the provided rules.
*
* @param array $data
* @param array $rules
* @param array $messages
* @param array $attributes
* @return array
*
* @throws \Illuminate\Validation\ValidationException
*/
public function validate(array $data, array $rules, array $messages = [], array $attributes = [])
{
return $this->make($data, $rules, $messages, $attributes)->validate();
}
/**
* Resolve a new Validator instance.
*
* @param array $data
* @param array $rules
* @param array $messages
* @param array $attributes
* @return \Illuminate\Validation\Validator
*/
protected function resolve(array $data, array $rules, array $messages, array $attributes)
{
if (is_null($this->resolver)) {
return new Validator($this->translator, $data, $rules, $messages, $attributes);
}
return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $attributes);
}
/**
* Add the extensions to a validator instance.
*
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
protected function addExtensions(Validator $validator)
{
$validator->addExtensions($this->extensions);
// Next, we will add the implicit extensions, which are similar to the required
// and accepted rule in that they're run even if the attributes aren't in an
// array of data which is given to a validator instance via instantiation.
$validator->addImplicitExtensions($this->implicitExtensions);
$validator->addDependentExtensions($this->dependentExtensions);
$validator->addReplacers($this->replacers);
$validator->setFallbackMessages($this->fallbackMessages);
}
/**
* Register a custom validator extension.
*
* @param string $rule
* @param \Closure|string $extension
* @param string|null $message
* @return void
*/
public function extend($rule, $extension, $message = null)
{
$this->extensions[$rule] = $extension;
if ($message) {
$this->fallbackMessages[Str::snake($rule)] = $message;
}
}
/**
* Register a custom implicit validator extension.
*
* @param string $rule
* @param \Closure|string $extension
* @param string|null $message
* @return void
*/
public function extendImplicit($rule, $extension, $message = null)
{
$this->implicitExtensions[$rule] = $extension;
if ($message) {
$this->fallbackMessages[Str::snake($rule)] = $message;
}
}
/**
* Register a custom dependent validator extension.
*
* @param string $rule
* @param \Closure|string $extension
* @param string|null $message
* @return void
*/
public function extendDependent($rule, $extension, $message = null)
{
$this->dependentExtensions[$rule] = $extension;
if ($message) {
$this->fallbackMessages[Str::snake($rule)] = $message;
}
}
/**
* Register a custom validator message replacer.
*
* @param string $rule
* @param \Closure|string $replacer
* @return void
*/
public function replacer($rule, $replacer)
{
$this->replacers[$rule] = $replacer;
}
/**
* Indicate that unvalidated array keys should be included in validated data when the parent array is validated.
*
* @return void
*/
public function includeUnvalidatedArrayKeys()
{
$this->excludeUnvalidatedArrayKeys = false;
}
/**
* Indicate that unvalidated array keys should be excluded from the validated data, even if the parent array was validated.
*
* @return void
*/
public function excludeUnvalidatedArrayKeys()
{
$this->excludeUnvalidatedArrayKeys = true;
}
/**
* Set the Validator instance resolver.
*
* @param \Closure $resolver
* @return void
*/
public function resolver(Closure $resolver)
{
$this->resolver = $resolver;
}
/**
* Get the Translator implementation.
*
* @return \Illuminate\Contracts\Translation\Translator
*/
public function getTranslator()
{
return $this->translator;
}
/**
* Get the Presence Verifier implementation.
*
* @return \Illuminate\Validation\PresenceVerifierInterface
*/
public function getPresenceVerifier()
{
return $this->verifier;
}
/**
* Set the Presence Verifier implementation.
*
* @param \Illuminate\Validation\PresenceVerifierInterface $presenceVerifier
* @return void
*/
public function setPresenceVerifier(PresenceVerifierInterface $presenceVerifier)
{
$this->verifier = $presenceVerifier;
}
/**
* Get the container instance used by the validation factory.
*
* @return \Illuminate\Contracts\Container\Container|null
*/
public function getContainer()
{
return $this->container;
}
/**
* Set the container instance used by the validation factory.
*
* @param \Illuminate\Contracts\Container\Container $container
* @return $this
*/
public function setContainer(Container $container)
{
$this->container = $container;
return $this;
}
}

View File

@@ -0,0 +1,156 @@
<?php
namespace Illuminate\Validation;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\ImplicitRule;
use Illuminate\Contracts\Validation\InvokableRule;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Translation\CreatesPotentiallyTranslatedStrings;
class InvokableValidationRule implements Rule, ValidatorAwareRule
{
use CreatesPotentiallyTranslatedStrings;
/**
* The invokable that validates the attribute.
*
* @var \Illuminate\Contracts\Validation\ValidationRule|\Illuminate\Contracts\Validation\InvokableRule
*/
protected $invokable;
/**
* Indicates if the validation invokable failed.
*
* @var bool
*/
protected $failed = false;
/**
* The validation error messages.
*
* @var array
*/
protected $messages = [];
/**
* The current validator.
*
* @var \Illuminate\Validation\Validator
*/
protected $validator;
/**
* The data under validation.
*
* @var array
*/
protected $data = [];
/**
* Create a new explicit Invokable validation rule.
*
* @param \Illuminate\Contracts\Validation\ValidationRule|\Illuminate\Contracts\Validation\InvokableRule $invokable
* @return void
*/
protected function __construct(ValidationRule|InvokableRule $invokable)
{
$this->invokable = $invokable;
}
/**
* Create a new implicit or explicit Invokable validation rule.
*
* @param \Illuminate\Contracts\Validation\ValidationRule|\Illuminate\Contracts\Validation\InvokableRule $invokable
* @return \Illuminate\Contracts\Validation\Rule|\Illuminate\Validation\InvokableValidationRule
*/
public static function make($invokable)
{
if ($invokable->implicit ?? false) {
return new class($invokable) extends InvokableValidationRule implements ImplicitRule {
};
}
return new InvokableValidationRule($invokable);
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$this->failed = false;
if ($this->invokable instanceof DataAwareRule) {
$this->invokable->setData($this->validator->getData());
}
if ($this->invokable instanceof ValidatorAwareRule) {
$this->invokable->setValidator($this->validator);
}
$method = $this->invokable instanceof ValidationRule
? 'validate'
: '__invoke';
$this->invokable->{$method}($attribute, $value, function ($attribute, $message = null) {
$this->failed = true;
return $this->pendingPotentiallyTranslatedString($attribute, $message);
});
return ! $this->failed;
}
/**
* Get the underlying invokable rule.
*
* @return \Illuminate\Contracts\Validation\ValidationRule|\Illuminate\Contracts\Validation\InvokableRule
*/
public function invokable()
{
return $this->invokable;
}
/**
* Get the validation error messages.
*
* @return array
*/
public function message()
{
return $this->messages;
}
/**
* Set the data under validation.
*
* @param array $data
* @return $this
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
/**
* Set the current validator.
*
* @param \Illuminate\Validation\Validator $validator
* @return $this
*/
public function setValidator($validator)
{
$this->validator = $validator;
return $this;
}
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,57 @@
<?php
namespace Illuminate\Validation;
use Illuminate\Support\Arr;
class NestedRules
{
/**
* The callback to execute.
*
* @var callable
*/
protected $callback;
/**
* Create a new nested rule instance.
*
* @param callable $callback
* @return void
*/
public function __construct(callable $callback)
{
$this->callback = $callback;
}
/**
* Compile the callback into an array of rules.
*
* @param string $attribute
* @param mixed $value
* @param mixed $data
* @return \stdClass
*/
public function compile($attribute, $value, $data = null)
{
$rules = call_user_func($this->callback, $value, $attribute, $data);
$parser = new ValidationRuleParser(
Arr::undot(Arr::wrap($data))
);
if (is_array($rules) && ! array_is_list($rules)) {
$nested = [];
foreach ($rules as $key => $rule) {
$nested[$attribute.'.'.$key] = $rule;
}
$rules = $nested;
} else {
$rules = [$attribute => $rules];
}
return $parser->explode(ValidationRuleParser::filterConditionalRules($rules, $data));
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace Illuminate\Validation;
use Exception;
use Illuminate\Contracts\Validation\UncompromisedVerifier;
use Illuminate\Support\Str;
class NotPwnedVerifier implements UncompromisedVerifier
{
/**
* The HTTP factory instance.
*
* @var \Illuminate\Http\Client\Factory
*/
protected $factory;
/**
* The number of seconds the request can run before timing out.
*
* @var int
*/
protected $timeout;
/**
* Create a new uncompromised verifier.
*
* @param \Illuminate\Http\Client\Factory $factory
* @param int|null $timeout
* @return void
*/
public function __construct($factory, $timeout = null)
{
$this->factory = $factory;
$this->timeout = $timeout ?? 30;
}
/**
* Verify that the given data has not been compromised in public breaches.
*
* @param array $data
* @return bool
*/
public function verify($data)
{
$value = $data['value'];
$threshold = $data['threshold'];
if (empty($value = (string) $value)) {
return false;
}
[$hash, $hashPrefix] = $this->getHash($value);
return ! $this->search($hashPrefix)
->contains(function ($line) use ($hash, $hashPrefix, $threshold) {
[$hashSuffix, $count] = explode(':', $line);
return $hashPrefix.$hashSuffix == $hash && $count > $threshold;
});
}
/**
* Get the hash and its first 5 chars.
*
* @param string $value
* @return array
*/
protected function getHash($value)
{
$hash = strtoupper(sha1((string) $value));
$hashPrefix = substr($hash, 0, 5);
return [$hash, $hashPrefix];
}
/**
* Search by the given hash prefix and returns all occurrences of leaked passwords.
*
* @param string $hashPrefix
* @return \Illuminate\Support\Collection
*/
protected function search($hashPrefix)
{
try {
$response = $this->factory->withHeaders([
'Add-Padding' => true,
])->timeout($this->timeout)->get(
'https://api.pwnedpasswords.com/range/'.$hashPrefix
);
} catch (Exception $e) {
report($e);
}
$body = (isset($response) && $response->successful())
? $response->body()
: '';
return Str::of($body)->trim()->explode("\n")->filter(function ($line) {
return str_contains($line, ':');
});
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Illuminate\Validation;
interface PresenceVerifierInterface
{
/**
* Count the number of objects in a collection having the given value.
*
* @param string $collection
* @param string $column
* @param string $value
* @param int|null $excludeId
* @param string|null $idColumn
* @param array $extra
* @return int
*/
public function getCount($collection, $column, $value, $excludeId = null, $idColumn = null, array $extra = []);
/**
* Count the number of objects in a collection with the given values.
*
* @param string $collection
* @param string $column
* @param array $values
* @param array $extra
* @return int
*/
public function getMultiCount($collection, $column, array $values, array $extra = []);
}

View File

@@ -0,0 +1,201 @@
<?php
namespace Illuminate\Validation;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Validation\Rules\Can;
use Illuminate\Validation\Rules\Dimensions;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rules\ExcludeIf;
use Illuminate\Validation\Rules\Exists;
use Illuminate\Validation\Rules\File;
use Illuminate\Validation\Rules\ImageFile;
use Illuminate\Validation\Rules\In;
use Illuminate\Validation\Rules\NotIn;
use Illuminate\Validation\Rules\ProhibitedIf;
use Illuminate\Validation\Rules\RequiredIf;
use Illuminate\Validation\Rules\Unique;
class Rule
{
use Macroable;
/**
* Get a can constraint builder instance.
*
* @param string $ability
* @param mixed ...$arguments
* @return \Illuminate\Validation\Rules\Can
*/
public static function can($ability, ...$arguments)
{
return new Can($ability, $arguments);
}
/**
* Apply the given rules if the given condition is truthy.
*
* @param callable|bool $condition
* @param array|string|\Closure $rules
* @param array|string|\Closure $defaultRules
* @return \Illuminate\Validation\ConditionalRules
*/
public static function when($condition, $rules, $defaultRules = [])
{
return new ConditionalRules($condition, $rules, $defaultRules);
}
/**
* Apply the given rules if the given condition is falsy.
*
* @param callable|bool $condition
* @param array|string|\Closure $rules
* @param array|string|\Closure $defaultRules
* @return \Illuminate\Validation\ConditionalRules
*/
public static function unless($condition, $rules, $defaultRules = [])
{
return new ConditionalRules(! $condition, $rules, $defaultRules);
}
/**
* Create a new nested rule set.
*
* @param callable $callback
* @return \Illuminate\Validation\NestedRules
*/
public static function forEach($callback)
{
return new NestedRules($callback);
}
/**
* Get a unique constraint builder instance.
*
* @param string $table
* @param string $column
* @return \Illuminate\Validation\Rules\Unique
*/
public static function unique($table, $column = 'NULL')
{
return new Unique($table, $column);
}
/**
* Get an exists constraint builder instance.
*
* @param string $table
* @param string $column
* @return \Illuminate\Validation\Rules\Exists
*/
public static function exists($table, $column = 'NULL')
{
return new Exists($table, $column);
}
/**
* Get an in constraint builder instance.
*
* @param \Illuminate\Contracts\Support\Arrayable|array|string $values
* @return \Illuminate\Validation\Rules\In
*/
public static function in($values)
{
if ($values instanceof Arrayable) {
$values = $values->toArray();
}
return new In(is_array($values) ? $values : func_get_args());
}
/**
* Get a not_in constraint builder instance.
*
* @param \Illuminate\Contracts\Support\Arrayable|array|string $values
* @return \Illuminate\Validation\Rules\NotIn
*/
public static function notIn($values)
{
if ($values instanceof Arrayable) {
$values = $values->toArray();
}
return new NotIn(is_array($values) ? $values : func_get_args());
}
/**
* Get a required_if constraint builder instance.
*
* @param callable|bool $callback
* @return \Illuminate\Validation\Rules\RequiredIf
*/
public static function requiredIf($callback)
{
return new RequiredIf($callback);
}
/**
* Get a exclude_if constraint builder instance.
*
* @param callable|bool $callback
* @return \Illuminate\Validation\Rules\ExcludeIf
*/
public static function excludeIf($callback)
{
return new ExcludeIf($callback);
}
/**
* Get a prohibited_if constraint builder instance.
*
* @param callable|bool $callback
* @return \Illuminate\Validation\Rules\ProhibitedIf
*/
public static function prohibitedIf($callback)
{
return new ProhibitedIf($callback);
}
/**
* Get an enum constraint builder instance.
*
* @param string $type
* @return \Illuminate\Validation\Rules\Enum
*/
public static function enum($type)
{
return new Enum($type);
}
/**
* Get a file constraint builder instance.
*
* @return \Illuminate\Validation\Rules\File
*/
public static function file()
{
return new File;
}
/**
* Get an image file constraint builder instance.
*
* @return \Illuminate\Validation\Rules\ImageFile
*/
public static function imageFile()
{
return new ImageFile;
}
/**
* Get a dimensions constraint builder instance.
*
* @param array $constraints
* @return \Illuminate\Validation\Rules\Dimensions
*/
public static function dimensions(array $constraints = [])
{
return new Dimensions($constraints);
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Illuminate\Validation\Rules;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Support\Facades\Gate;
class Can implements Rule, ValidatorAwareRule
{
/**
* The ability to check.
*
* @var string
*/
protected $ability;
/**
* The arguments to pass to the authorization check.
*
* @var array
*/
protected $arguments;
/**
* The current validator instance.
*
* @var \Illuminate\Validation\Validator
*/
protected $validator;
/**
* Constructor.
*
* @param string $ability
* @param array $arguments
*/
public function __construct($ability, array $arguments = [])
{
$this->ability = $ability;
$this->arguments = $arguments;
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$arguments = $this->arguments;
$model = array_shift($arguments);
return Gate::allows($this->ability, array_filter([$model, ...$arguments, $value]));
}
/**
* Get the validation error message.
*
* @return array
*/
public function message()
{
$message = $this->validator->getTranslator()->get('validation.can');
return $message === 'validation.can'
? ['The :attribute field contains an unauthorized value.']
: $message;
}
/**
* Set the current validator.
*
* @param \Illuminate\Validation\Validator $validator
* @return $this
*/
public function setValidator($validator)
{
$this->validator = $validator;
return $this;
}
}

View File

@@ -0,0 +1,241 @@
<?php
namespace Illuminate\Validation\Rules;
use BackedEnum;
use Closure;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Eloquent\Model;
trait DatabaseRule
{
/**
* The table to run the query against.
*
* @var string
*/
protected $table;
/**
* The column to check on.
*
* @var string
*/
protected $column;
/**
* The extra where clauses for the query.
*
* @var array
*/
protected $wheres = [];
/**
* The array of custom query callbacks.
*
* @var array
*/
protected $using = [];
/**
* Create a new rule instance.
*
* @param string $table
* @param string $column
* @return void
*/
public function __construct($table, $column = 'NULL')
{
$this->column = $column;
$this->table = $this->resolveTableName($table);
}
/**
* Resolves the name of the table from the given string.
*
* @param string $table
* @return string
*/
public function resolveTableName($table)
{
if (! str_contains($table, '\\') || ! class_exists($table)) {
return $table;
}
if (is_subclass_of($table, Model::class)) {
$model = new $table;
if (str_contains($model->getTable(), '.')) {
return $table;
}
return implode('.', array_map(function (string $part) {
return trim($part, '.');
}, array_filter([$model->getConnectionName(), $model->getTable()])));
}
return $table;
}
/**
* Set a "where" constraint on the query.
*
* @param \Closure|string $column
* @param \Illuminate\Contracts\Support\Arrayable|\BackedEnum|\Closure|array|string|int|bool|null $value
* @return $this
*/
public function where($column, $value = null)
{
if ($value instanceof Arrayable || is_array($value)) {
return $this->whereIn($column, $value);
}
if ($column instanceof Closure) {
return $this->using($column);
}
if (is_null($value)) {
return $this->whereNull($column);
}
if ($value instanceof BackedEnum) {
$value = $value->value;
}
$this->wheres[] = compact('column', 'value');
return $this;
}
/**
* Set a "where not" constraint on the query.
*
* @param string $column
* @param \Illuminate\Contracts\Support\Arrayable|\BackedEnum|array|string $value
* @return $this
*/
public function whereNot($column, $value)
{
if ($value instanceof Arrayable || is_array($value)) {
return $this->whereNotIn($column, $value);
}
if ($value instanceof BackedEnum) {
$value = $value->value;
}
return $this->where($column, '!'.$value);
}
/**
* Set a "where null" constraint on the query.
*
* @param string $column
* @return $this
*/
public function whereNull($column)
{
return $this->where($column, 'NULL');
}
/**
* Set a "where not null" constraint on the query.
*
* @param string $column
* @return $this
*/
public function whereNotNull($column)
{
return $this->where($column, 'NOT_NULL');
}
/**
* Set a "where in" constraint on the query.
*
* @param string $column
* @param \Illuminate\Contracts\Support\Arrayable|\BackedEnum|array $values
* @return $this
*/
public function whereIn($column, $values)
{
return $this->where(function ($query) use ($column, $values) {
$query->whereIn($column, $values);
});
}
/**
* Set a "where not in" constraint on the query.
*
* @param string $column
* @param \Illuminate\Contracts\Support\Arrayable|\BackedEnum|array $values
* @return $this
*/
public function whereNotIn($column, $values)
{
return $this->where(function ($query) use ($column, $values) {
$query->whereNotIn($column, $values);
});
}
/**
* Ignore soft deleted models during the existence check.
*
* @param string $deletedAtColumn
* @return $this
*/
public function withoutTrashed($deletedAtColumn = 'deleted_at')
{
$this->whereNull($deletedAtColumn);
return $this;
}
/**
* Only include soft deleted models during the existence check.
*
* @param string $deletedAtColumn
* @return $this
*/
public function onlyTrashed($deletedAtColumn = 'deleted_at')
{
$this->whereNotNull($deletedAtColumn);
return $this;
}
/**
* Register a custom query callback.
*
* @param \Closure $callback
* @return $this
*/
public function using(Closure $callback)
{
$this->using[] = $callback;
return $this;
}
/**
* Get the custom query callbacks for the rule.
*
* @return array
*/
public function queryCallbacks()
{
return $this->using;
}
/**
* Format the where clauses.
*
* @return string
*/
protected function formatWheres()
{
return collect($this->wheres)->map(function ($where) {
return $where['column'].','.'"'.str_replace('"', '""', $where['value']).'"';
})->implode(',');
}
}

View File

@@ -0,0 +1,135 @@
<?php
namespace Illuminate\Validation\Rules;
use Illuminate\Support\Traits\Conditionable;
class Dimensions
{
use Conditionable;
/**
* The constraints for the dimensions rule.
*
* @var array
*/
protected $constraints = [];
/**
* Create a new dimensions rule instance.
*
* @param array $constraints
* @return void
*/
public function __construct(array $constraints = [])
{
$this->constraints = $constraints;
}
/**
* Set the "width" constraint.
*
* @param int $value
* @return $this
*/
public function width($value)
{
$this->constraints['width'] = $value;
return $this;
}
/**
* Set the "height" constraint.
*
* @param int $value
* @return $this
*/
public function height($value)
{
$this->constraints['height'] = $value;
return $this;
}
/**
* Set the "min width" constraint.
*
* @param int $value
* @return $this
*/
public function minWidth($value)
{
$this->constraints['min_width'] = $value;
return $this;
}
/**
* Set the "min height" constraint.
*
* @param int $value
* @return $this
*/
public function minHeight($value)
{
$this->constraints['min_height'] = $value;
return $this;
}
/**
* Set the "max width" constraint.
*
* @param int $value
* @return $this
*/
public function maxWidth($value)
{
$this->constraints['max_width'] = $value;
return $this;
}
/**
* Set the "max height" constraint.
*
* @param int $value
* @return $this
*/
public function maxHeight($value)
{
$this->constraints['max_height'] = $value;
return $this;
}
/**
* Set the "ratio" constraint.
*
* @param float $value
* @return $this
*/
public function ratio($value)
{
$this->constraints['ratio'] = $value;
return $this;
}
/**
* Convert the rule to a validation string.
*
* @return string
*/
public function __toString()
{
$result = '';
foreach ($this->constraints as $key => $value) {
$result .= "$key=$value,";
}
return 'dimensions:'.substr($result, 0, -1);
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Illuminate\Validation\Rules;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use TypeError;
class Enum implements Rule, ValidatorAwareRule
{
/**
* The type of the enum.
*
* @var string
*/
protected $type;
/**
* The current validator instance.
*
* @var \Illuminate\Validation\Validator
*/
protected $validator;
/**
* Create a new rule instance.
*
* @param string $type
* @return void
*/
public function __construct($type)
{
$this->type = $type;
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
if ($value instanceof $this->type) {
return true;
}
if (is_null($value) || ! enum_exists($this->type) || ! method_exists($this->type, 'tryFrom')) {
return false;
}
try {
return ! is_null($this->type::tryFrom($value));
} catch (TypeError) {
return false;
}
}
/**
* Get the validation error message.
*
* @return array
*/
public function message()
{
$message = $this->validator->getTranslator()->get('validation.enum');
return $message === 'validation.enum'
? ['The selected :attribute is invalid.']
: $message;
}
/**
* Set the current validator.
*
* @param \Illuminate\Validation\Validator $validator
* @return $this
*/
public function setValidator($validator)
{
$this->validator = $validator;
return $this;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Illuminate\Validation\Rules;
use Closure;
use InvalidArgumentException;
class ExcludeIf
{
/**
* The condition that validates the attribute.
*
* @var \Closure|bool
*/
public $condition;
/**
* Create a new exclude validation rule based on a condition.
*
* @param \Closure|bool $condition
* @return void
*
* @throws \InvalidArgumentException
*/
public function __construct($condition)
{
if ($condition instanceof Closure || is_bool($condition)) {
$this->condition = $condition;
} else {
throw new InvalidArgumentException('The provided condition must be a callable or boolean.');
}
}
/**
* Convert the rule to a validation string.
*
* @return string
*/
public function __toString()
{
if (is_callable($this->condition)) {
return call_user_func($this->condition) ? 'exclude' : '';
}
return $this->condition ? 'exclude' : '';
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Illuminate\Validation\Rules;
use Illuminate\Support\Traits\Conditionable;
class Exists
{
use Conditionable, DatabaseRule;
/**
* Convert the rule to a validation string.
*
* @return string
*/
public function __toString()
{
return rtrim(sprintf('exists:%s,%s,%s',
$this->table,
$this->column,
$this->formatWheres()
), ',');
}
}

View File

@@ -0,0 +1,377 @@
<?php
namespace Illuminate\Validation\Rules;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Conditionable;
use Illuminate\Support\Traits\Macroable;
use InvalidArgumentException;
class File implements Rule, DataAwareRule, ValidatorAwareRule
{
use Conditionable;
use Macroable;
/**
* The MIME types that the given file should match. This array may also contain file extensions.
*
* @var array
*/
protected $allowedMimetypes = [];
/**
* The extensions that the given file should match.
*
* @var array
*/
protected $allowedExtensions = [];
/**
* The minimum size in kilobytes that the file can be.
*
* @var null|int
*/
protected $minimumFileSize = null;
/**
* The maximum size in kilobytes that the file can be.
*
* @var null|int
*/
protected $maximumFileSize = null;
/**
* An array of custom rules that will be merged into the validation rules.
*
* @var array
*/
protected $customRules = [];
/**
* The error message after validation, if any.
*
* @var array
*/
protected $messages = [];
/**
* The data under validation.
*
* @var array
*/
protected $data;
/**
* The validator performing the validation.
*
* @var \Illuminate\Validation\Validator
*/
protected $validator;
/**
* The callback that will generate the "default" version of the file rule.
*
* @var string|array|callable|null
*/
public static $defaultCallback;
/**
* Set the default callback to be used for determining the file default rules.
*
* If no arguments are passed, the default file rule configuration will be returned.
*
* @param static|callable|null $callback
* @return static|null
*/
public static function defaults($callback = null)
{
if (is_null($callback)) {
return static::default();
}
if (! is_callable($callback) && ! $callback instanceof static) {
throw new InvalidArgumentException('The given callback should be callable or an instance of '.static::class);
}
static::$defaultCallback = $callback;
}
/**
* Get the default configuration of the file rule.
*
* @return static
*/
public static function default()
{
$file = is_callable(static::$defaultCallback)
? call_user_func(static::$defaultCallback)
: static::$defaultCallback;
return $file instanceof Rule ? $file : new self();
}
/**
* Limit the uploaded file to only image types.
*
* @return ImageFile
*/
public static function image()
{
return new ImageFile();
}
/**
* Limit the uploaded file to the given MIME types or file extensions.
*
* @param string|array<int, string> $mimetypes
* @return static
*/
public static function types($mimetypes)
{
return tap(new static(), fn ($file) => $file->allowedMimetypes = (array) $mimetypes);
}
/**
* Limit the uploaded file to the given file extensions.
*
* @param string|array<int, string> $extensions
* @return $this
*/
public function extensions($extensions)
{
$this->allowedExtensions = (array) $extensions;
return $this;
}
/**
* Indicate that the uploaded file should be exactly a certain size in kilobytes.
*
* @param string|int $size
* @return $this
*/
public function size($size)
{
$this->minimumFileSize = $this->toKilobytes($size);
$this->maximumFileSize = $this->minimumFileSize;
return $this;
}
/**
* Indicate that the uploaded file should be between a minimum and maximum size in kilobytes.
*
* @param string|int $minSize
* @param string|int $maxSize
* @return $this
*/
public function between($minSize, $maxSize)
{
$this->minimumFileSize = $this->toKilobytes($minSize);
$this->maximumFileSize = $this->toKilobytes($maxSize);
return $this;
}
/**
* Indicate that the uploaded file should be no less than the given number of kilobytes.
*
* @param string|int $size
* @return $this
*/
public function min($size)
{
$this->minimumFileSize = $this->toKilobytes($size);
return $this;
}
/**
* Indicate that the uploaded file should be no more than the given number of kilobytes.
*
* @param string|int $size
* @return $this
*/
public function max($size)
{
$this->maximumFileSize = $this->toKilobytes($size);
return $this;
}
/**
* Convert a potentially human-friendly file size to kilobytes.
*
* @param string|int $size
* @return mixed
*/
protected function toKilobytes($size)
{
if (! is_string($size)) {
return $size;
}
$value = floatval($size);
return round(match (true) {
Str::endsWith($size, 'kb') => $value * 1,
Str::endsWith($size, 'mb') => $value * 1000,
Str::endsWith($size, 'gb') => $value * 1000000,
Str::endsWith($size, 'tb') => $value * 1000000000,
default => throw new InvalidArgumentException('Invalid file size suffix.'),
});
}
/**
* Specify additional validation rules that should be merged with the default rules during validation.
*
* @param string|array $rules
* @return $this
*/
public function rules($rules)
{
$this->customRules = array_merge($this->customRules, Arr::wrap($rules));
return $this;
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$this->messages = [];
$validator = Validator::make(
$this->data,
[$attribute => $this->buildValidationRules()],
$this->validator->customMessages,
$this->validator->customAttributes
);
if ($validator->fails()) {
return $this->fail($validator->messages()->all());
}
return true;
}
/**
* Build the array of underlying validation rules based on the current state.
*
* @return array
*/
protected function buildValidationRules()
{
$rules = ['file'];
$rules = array_merge($rules, $this->buildMimetypes());
if (! empty($this->allowedExtensions)) {
$rules[] = 'extensions:'.implode(',', array_map('strtolower', $this->allowedExtensions));
}
$rules[] = match (true) {
is_null($this->minimumFileSize) && is_null($this->maximumFileSize) => null,
is_null($this->maximumFileSize) => "min:{$this->minimumFileSize}",
is_null($this->minimumFileSize) => "max:{$this->maximumFileSize}",
$this->minimumFileSize !== $this->maximumFileSize => "between:{$this->minimumFileSize},{$this->maximumFileSize}",
default => "size:{$this->minimumFileSize}",
};
return array_merge(array_filter($rules), $this->customRules);
}
/**
* Separate the given mimetypes from extensions and return an array of correct rules to validate against.
*
* @return array
*/
protected function buildMimetypes()
{
if (count($this->allowedMimetypes) === 0) {
return [];
}
$rules = [];
$mimetypes = array_filter(
$this->allowedMimetypes,
fn ($type) => str_contains($type, '/')
);
$mimes = array_diff($this->allowedMimetypes, $mimetypes);
if (count($mimetypes) > 0) {
$rules[] = 'mimetypes:'.implode(',', $mimetypes);
}
if (count($mimes) > 0) {
$rules[] = 'mimes:'.implode(',', $mimes);
}
return $rules;
}
/**
* Adds the given failures, and return false.
*
* @param array|string $messages
* @return bool
*/
protected function fail($messages)
{
$messages = collect(Arr::wrap($messages))->map(function ($message) {
return $this->validator->getTranslator()->get($message);
})->all();
$this->messages = array_merge($this->messages, $messages);
return false;
}
/**
* Get the validation error message.
*
* @return array
*/
public function message()
{
return $this->messages;
}
/**
* Set the current validator.
*
* @param \Illuminate\Contracts\Validation\Validator $validator
* @return $this
*/
public function setValidator($validator)
{
$this->validator = $validator;
return $this;
}
/**
* Set the current data under validation.
*
* @param array $data
* @return $this
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Illuminate\Validation\Rules;
class ImageFile extends File
{
/**
* Create a new image file rule instance.
*
* @return void
*/
public function __construct()
{
$this->rules('image');
}
/**
* The dimension constraints for the uploaded file.
*
* @param \Illuminate\Validation\Rules\Dimensions $dimensions
*/
public function dimensions($dimensions)
{
$this->rules($dimensions);
return $this;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Illuminate\Validation\Rules;
use BackedEnum;
use Illuminate\Contracts\Support\Arrayable;
use UnitEnum;
class In
{
/**
* The name of the rule.
*
* @var string
*/
protected $rule = 'in';
/**
* The accepted values.
*
* @var array
*/
protected $values;
/**
* Create a new in rule instance.
*
* @param \Illuminate\Contracts\Support\Arrayable|array|string $values
* @return void
*/
public function __construct($values)
{
if ($values instanceof Arrayable) {
$values = $values->toArray();
}
$this->values = is_array($values) ? $values : func_get_args();
}
/**
* Convert the rule to a validation string.
*
* @return string
*
* @see \Illuminate\Validation\ValidationRuleParser::parseParameters
*/
public function __toString()
{
$values = array_map(function ($value) {
$value = match (true) {
$value instanceof BackedEnum => $value->value,
$value instanceof UnitEnum => $value->name,
default => $value,
};
return '"'.str_replace('"', '""', $value).'"';
}, $this->values);
return $this->rule.':'.implode(',', $values);
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Illuminate\Validation\Rules;
use BackedEnum;
use Illuminate\Contracts\Support\Arrayable;
use UnitEnum;
class NotIn
{
/**
* The name of the rule.
*
* @var string
*/
protected $rule = 'not_in';
/**
* The accepted values.
*
* @var array
*/
protected $values;
/**
* Create a new "not in" rule instance.
*
* @param \Illuminate\Contracts\Support\Arrayable|array|string $values
* @return void
*/
public function __construct($values)
{
if ($values instanceof Arrayable) {
$values = $values->toArray();
}
$this->values = is_array($values) ? $values : func_get_args();
}
/**
* Convert the rule to a validation string.
*
* @return string
*/
public function __toString()
{
$values = array_map(function ($value) {
$value = match (true) {
$value instanceof BackedEnum => $value->value,
$value instanceof UnitEnum => $value->name,
default => $value,
};
return '"'.str_replace('"', '""', $value).'"';
}, $this->values);
return $this->rule.':'.implode(',', $values);
}
}

View File

@@ -0,0 +1,358 @@
<?php
namespace Illuminate\Validation\Rules;
use Illuminate\Container\Container;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\UncompromisedVerifier;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Traits\Conditionable;
use InvalidArgumentException;
class Password implements Rule, DataAwareRule, ValidatorAwareRule
{
use Conditionable;
/**
* The validator performing the validation.
*
* @var \Illuminate\Contracts\Validation\Validator
*/
protected $validator;
/**
* The data under validation.
*
* @var array
*/
protected $data;
/**
* The minimum size of the password.
*
* @var int
*/
protected $min = 8;
/**
* If the password requires at least one uppercase and one lowercase letter.
*
* @var bool
*/
protected $mixedCase = false;
/**
* If the password requires at least one letter.
*
* @var bool
*/
protected $letters = false;
/**
* If the password requires at least one number.
*
* @var bool
*/
protected $numbers = false;
/**
* If the password requires at least one symbol.
*
* @var bool
*/
protected $symbols = false;
/**
* If the password should not have been compromised in data leaks.
*
* @var bool
*/
protected $uncompromised = false;
/**
* The number of times a password can appear in data leaks before being considered compromised.
*
* @var int
*/
protected $compromisedThreshold = 0;
/**
* Additional validation rules that should be merged into the default rules during validation.
*
* @var array
*/
protected $customRules = [];
/**
* The failure messages, if any.
*
* @var array
*/
protected $messages = [];
/**
* The callback that will generate the "default" version of the password rule.
*
* @var string|array|callable|null
*/
public static $defaultCallback;
/**
* Create a new rule instance.
*
* @param int $min
* @return void
*/
public function __construct($min)
{
$this->min = max((int) $min, 1);
}
/**
* Set the default callback to be used for determining a password's default rules.
*
* If no arguments are passed, the default password rule configuration will be returned.
*
* @param static|callable|null $callback
* @return static|null
*/
public static function defaults($callback = null)
{
if (is_null($callback)) {
return static::default();
}
if (! is_callable($callback) && ! $callback instanceof static) {
throw new InvalidArgumentException('The given callback should be callable or an instance of '.static::class);
}
static::$defaultCallback = $callback;
}
/**
* Get the default configuration of the password rule.
*
* @return static
*/
public static function default()
{
$password = is_callable(static::$defaultCallback)
? call_user_func(static::$defaultCallback)
: static::$defaultCallback;
return $password instanceof Rule ? $password : static::min(8);
}
/**
* Get the default configuration of the password rule and mark the field as required.
*
* @return array
*/
public static function required()
{
return ['required', static::default()];
}
/**
* Get the default configuration of the password rule and mark the field as sometimes being required.
*
* @return array
*/
public static function sometimes()
{
return ['sometimes', static::default()];
}
/**
* Set the performing validator.
*
* @param \Illuminate\Contracts\Validation\Validator $validator
* @return $this
*/
public function setValidator($validator)
{
$this->validator = $validator;
return $this;
}
/**
* Set the data under validation.
*
* @param array $data
* @return $this
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
/**
* Sets the minimum size of the password.
*
* @param int $size
* @return $this
*/
public static function min($size)
{
return new static($size);
}
/**
* Ensures the password has not been compromised in data leaks.
*
* @param int $threshold
* @return $this
*/
public function uncompromised($threshold = 0)
{
$this->uncompromised = true;
$this->compromisedThreshold = $threshold;
return $this;
}
/**
* Makes the password require at least one uppercase and one lowercase letter.
*
* @return $this
*/
public function mixedCase()
{
$this->mixedCase = true;
return $this;
}
/**
* Makes the password require at least one letter.
*
* @return $this
*/
public function letters()
{
$this->letters = true;
return $this;
}
/**
* Makes the password require at least one number.
*
* @return $this
*/
public function numbers()
{
$this->numbers = true;
return $this;
}
/**
* Makes the password require at least one symbol.
*
* @return $this
*/
public function symbols()
{
$this->symbols = true;
return $this;
}
/**
* Specify additional validation rules that should be merged with the default rules during validation.
*
* @param \Closure|string|array $rules
* @return $this
*/
public function rules($rules)
{
$this->customRules = Arr::wrap($rules);
return $this;
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$this->messages = [];
$validator = Validator::make(
$this->data,
[$attribute => array_merge(['string', 'min:'.$this->min], $this->customRules)],
$this->validator->customMessages,
$this->validator->customAttributes
)->after(function ($validator) use ($attribute, $value) {
if (! is_string($value)) {
return;
}
if ($this->mixedCase && ! preg_match('/(\p{Ll}+.*\p{Lu})|(\p{Lu}+.*\p{Ll})/u', $value)) {
$validator->addFailure($attribute, 'password.mixed');
}
if ($this->letters && ! preg_match('/\pL/u', $value)) {
$validator->addFailure($attribute, 'password.letters');
}
if ($this->symbols && ! preg_match('/\p{Z}|\p{S}|\p{P}/u', $value)) {
$validator->addFailure($attribute, 'password.symbols');
}
if ($this->numbers && ! preg_match('/\pN/u', $value)) {
$validator->addFailure($attribute, 'password.numbers');
}
});
if ($validator->fails()) {
return $this->fail($validator->messages()->all());
}
if ($this->uncompromised && ! Container::getInstance()->make(UncompromisedVerifier::class)->verify([
'value' => $value,
'threshold' => $this->compromisedThreshold,
])) {
$validator->addFailure($attribute, 'password.uncompromised');
return $this->fail($validator->messages()->all());
}
return true;
}
/**
* Get the validation error message.
*
* @return array
*/
public function message()
{
return $this->messages;
}
/**
* Adds the given failures, and return false.
*
* @param array|string $messages
* @return bool
*/
protected function fail($messages)
{
$this->messages = array_merge($this->messages, Arr::wrap($messages));
return false;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Illuminate\Validation\Rules;
use Closure;
use InvalidArgumentException;
class ProhibitedIf
{
/**
* The condition that validates the attribute.
*
* @var \Closure|bool
*/
public $condition;
/**
* Create a new prohibited validation rule based on a condition.
*
* @param \Closure|bool $condition
* @return void
*
* @throws \InvalidArgumentException
*/
public function __construct($condition)
{
if ($condition instanceof Closure || is_bool($condition)) {
$this->condition = $condition;
} else {
throw new InvalidArgumentException('The provided condition must be a callable or boolean.');
}
}
/**
* Convert the rule to a validation string.
*
* @return string
*/
public function __toString()
{
if (is_callable($this->condition)) {
return call_user_func($this->condition) ? 'prohibited' : '';
}
return $this->condition ? 'prohibited' : '';
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Illuminate\Validation\Rules;
use InvalidArgumentException;
class RequiredIf
{
/**
* The condition that validates the attribute.
*
* @var callable|bool
*/
public $condition;
/**
* Create a new required validation rule based on a condition.
*
* @param callable|bool $condition
* @return void
*/
public function __construct($condition)
{
if (! is_string($condition)) {
$this->condition = $condition;
} else {
throw new InvalidArgumentException('The provided condition must be a callable or boolean.');
}
}
/**
* Convert the rule to a validation string.
*
* @return string
*/
public function __toString()
{
if (is_callable($this->condition)) {
return call_user_func($this->condition) ? 'required' : '';
}
return $this->condition ? 'required' : '';
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace Illuminate\Validation\Rules;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Traits\Conditionable;
class Unique
{
use Conditionable, DatabaseRule;
/**
* The ID that should be ignored.
*
* @var mixed
*/
protected $ignore;
/**
* The name of the ID column.
*
* @var string
*/
protected $idColumn = 'id';
/**
* Ignore the given ID during the unique check.
*
* @param mixed $id
* @param string|null $idColumn
* @return $this
*/
public function ignore($id, $idColumn = null)
{
if ($id instanceof Model) {
return $this->ignoreModel($id, $idColumn);
}
$this->ignore = $id;
$this->idColumn = $idColumn ?? 'id';
return $this;
}
/**
* Ignore the given model during the unique check.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string|null $idColumn
* @return $this
*/
public function ignoreModel($model, $idColumn = null)
{
$this->idColumn = $idColumn ?? $model->getKeyName();
$this->ignore = $model->{$this->idColumn};
return $this;
}
/**
* Convert the rule to a validation string.
*
* @return string
*/
public function __toString()
{
return rtrim(sprintf('unique:%s,%s,%s,%s,%s',
$this->table,
$this->column,
$this->ignore ? '"'.addslashes($this->ignore).'"' : 'NULL',
$this->idColumn,
$this->formatWheres()
), ',');
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Illuminate\Validation;
use RuntimeException;
class UnauthorizedException extends RuntimeException
{
//
}

View File

@@ -0,0 +1,108 @@
<?php
namespace Illuminate\Validation;
use Illuminate\Foundation\Precognition;
/**
* Provides default implementation of ValidatesWhenResolved contract.
*/
trait ValidatesWhenResolvedTrait
{
/**
* Validate the class instance.
*
* @return void
*/
public function validateResolved()
{
$this->prepareForValidation();
if (! $this->passesAuthorization()) {
$this->failedAuthorization();
}
$instance = $this->getValidatorInstance();
if ($this->isPrecognitive()) {
$instance->after(Precognition::afterValidationHook($this));
}
if ($instance->fails()) {
$this->failedValidation($instance);
}
$this->passedValidation();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
//
}
/**
* Get the validator instance for the request.
*
* @return \Illuminate\Validation\Validator
*/
protected function getValidatorInstance()
{
return $this->validator();
}
/**
* Handle a passed validation attempt.
*
* @return void
*/
protected function passedValidation()
{
//
}
/**
* Handle a failed validation attempt.
*
* @param \Illuminate\Validation\Validator $validator
* @return void
*
* @throws \Illuminate\Validation\ValidationException
*/
protected function failedValidation(Validator $validator)
{
$exception = $validator->getException();
throw new $exception($validator);
}
/**
* Determine if the request passes the authorization check.
*
* @return bool
*/
protected function passesAuthorization()
{
if (method_exists($this, 'authorize')) {
return $this->authorize();
}
return true;
}
/**
* Handle a failed authorization attempt.
*
* @return void
*
* @throws \Illuminate\Validation\UnauthorizedException
*/
protected function failedAuthorization()
{
throw new UnauthorizedException;
}
}

View File

@@ -0,0 +1,112 @@
<?php
namespace Illuminate\Validation;
use Illuminate\Support\Arr;
class ValidationData
{
/**
* Initialize and gather data for the given attribute.
*
* @param string $attribute
* @param array $masterData
* @return array
*/
public static function initializeAndGatherData($attribute, $masterData)
{
$data = Arr::dot(static::initializeAttributeOnData($attribute, $masterData));
return array_merge($data, static::extractValuesForWildcards(
$masterData, $data, $attribute
));
}
/**
* Gather a copy of the attribute data filled with any missing attributes.
*
* @param string $attribute
* @param array $masterData
* @return array
*/
protected static function initializeAttributeOnData($attribute, $masterData)
{
$explicitPath = static::getLeadingExplicitAttributePath($attribute);
$data = static::extractDataFromPath($explicitPath, $masterData);
if (! str_contains($attribute, '*') || str_ends_with($attribute, '*')) {
return $data;
}
return data_set($data, $attribute, null, true);
}
/**
* Get all of the exact attribute values for a given wildcard attribute.
*
* @param array $masterData
* @param array $data
* @param string $attribute
* @return array
*/
protected static function extractValuesForWildcards($masterData, $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] = Arr::get($masterData, $key);
}
return $data;
}
/**
* Extract data based on the given dot-notated path.
*
* Used to extract a sub-section of the data for faster iteration.
*
* @param string $attribute
* @param array $masterData
* @return array
*/
public static function extractDataFromPath($attribute, $masterData)
{
$results = [];
$value = Arr::get($masterData, $attribute, '__missing__');
if ($value !== '__missing__') {
Arr::set($results, $attribute, $value);
}
return $results;
}
/**
* Get the explicit part of the attribute name.
*
* E.g. 'foo.bar.*.baz' -> 'foo.bar'
*
* Allows us to not spin through all of the flattened data for some operations.
*
* @param string $attribute
* @return string
*/
public static function getLeadingExplicitAttributePath($attribute)
{
return rtrim(explode('*', $attribute)[0], '.') ?: null;
}
}

View File

@@ -0,0 +1,163 @@
<?php
namespace Illuminate\Validation;
use Exception;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator as ValidatorFacade;
class ValidationException extends Exception
{
/**
* The validator instance.
*
* @var \Illuminate\Contracts\Validation\Validator
*/
public $validator;
/**
* The recommended response to send to the client.
*
* @var \Symfony\Component\HttpFoundation\Response|null
*/
public $response;
/**
* The status code to use for the response.
*
* @var int
*/
public $status = 422;
/**
* The name of the error bag.
*
* @var string
*/
public $errorBag;
/**
* The path the client should be redirected to.
*
* @var string
*/
public $redirectTo;
/**
* Create a new exception instance.
*
* @param \Illuminate\Contracts\Validation\Validator $validator
* @param \Symfony\Component\HttpFoundation\Response|null $response
* @param string $errorBag
* @return void
*/
public function __construct($validator, $response = null, $errorBag = 'default')
{
parent::__construct(static::summarize($validator));
$this->response = $response;
$this->errorBag = $errorBag;
$this->validator = $validator;
}
/**
* Create a new validation exception from a plain array of messages.
*
* @param array $messages
* @return static
*/
public static function withMessages(array $messages)
{
return new static(tap(ValidatorFacade::make([], []), function ($validator) use ($messages) {
foreach ($messages as $key => $value) {
foreach (Arr::wrap($value) as $message) {
$validator->errors()->add($key, $message);
}
}
}));
}
/**
* Create an error message summary from the validation errors.
*
* @param \Illuminate\Contracts\Validation\Validator $validator
* @return string
*/
protected static function summarize($validator)
{
$messages = $validator->errors()->all();
if (! count($messages) || ! is_string($messages[0])) {
return $validator->getTranslator()->get('The given data was invalid.');
}
$message = array_shift($messages);
if ($count = count($messages)) {
$pluralized = $count === 1 ? 'error' : 'errors';
$message .= ' '.$validator->getTranslator()->get("(and :count more $pluralized)", compact('count'));
}
return $message;
}
/**
* Get all of the validation error messages.
*
* @return array
*/
public function errors()
{
return $this->validator->errors()->messages();
}
/**
* Set the HTTP status code to be used for the response.
*
* @param int $status
* @return $this
*/
public function status($status)
{
$this->status = $status;
return $this;
}
/**
* Set the error bag on the exception.
*
* @param string $errorBag
* @return $this
*/
public function errorBag($errorBag)
{
$this->errorBag = $errorBag;
return $this;
}
/**
* Set the URL to redirect to on a validation error.
*
* @param string $url
* @return $this
*/
public function redirectTo($url)
{
$this->redirectTo = $url;
return $this;
}
/**
* Get the underlying response instance.
*
* @return \Symfony\Component\HttpFoundation\Response|null
*/
public function getResponse()
{
return $this->response;
}
}

View File

@@ -0,0 +1,342 @@
<?php
namespace Illuminate\Validation;
use Closure;
use Illuminate\Contracts\Validation\InvokableRule;
use Illuminate\Contracts\Validation\Rule as RuleContract;
use Illuminate\Contracts\Validation\ValidationRule;
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, $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)) {
return Arr::wrap($this->prepareRule($rule, $attribute));
}
return array_map(
[$this, 'prepareRule'],
$rule,
array_fill((int) array_key_first($rule), count($rule), $attribute)
);
}
/**
* 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 NestedRules) {
return $rule->compile(
$attribute, $this->data[$attribute] ?? null, Arr::dot($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 NestedRules) {
$compiled = $rule->compile($key, $value, $data);
$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 NestedRules) {
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);
}
/**
* 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 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($data))
: array_filter($attributeRules->defaultRules($data)), ];
}
return [$attribute => collect($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();
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Illuminate\Validation;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Contracts\Validation\UncompromisedVerifier;
use Illuminate\Http\Client\Factory as HttpFactory;
use Illuminate\Support\ServiceProvider;
class ValidationServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerPresenceVerifier();
$this->registerUncompromisedVerifier();
$this->registerValidationFactory();
}
/**
* Register the validation factory.
*
* @return void
*/
protected function registerValidationFactory()
{
$this->app->singleton('validator', function ($app) {
$validator = new Factory($app['translator'], $app);
// The validation presence verifier is responsible for determining the existence of
// values in a given data collection which is typically a relational database or
// other persistent data stores. It is used to check for "uniqueness" as well.
if (isset($app['db'], $app['validation.presence'])) {
$validator->setPresenceVerifier($app['validation.presence']);
}
return $validator;
});
}
/**
* Register the database presence verifier.
*
* @return void
*/
protected function registerPresenceVerifier()
{
$this->app->singleton('validation.presence', function ($app) {
return new DatabasePresenceVerifier($app['db']);
});
}
/**
* Register the uncompromised password verifier.
*
* @return void
*/
protected function registerUncompromisedVerifier()
{
$this->app->singleton(UncompromisedVerifier::class, function ($app) {
return new NotPwnedVerifier($app[HttpFactory::class]);
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return ['validator', 'validation.presence', UncompromisedVerifier::class];
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
{
"name": "illuminate/validation",
"description": "The Illuminate Validation package.",
"license": "MIT",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"require": {
"php": "^8.1",
"ext-filter": "*",
"ext-mbstring": "*",
"brick/math": "^0.9.3|^0.10.2|^0.11",
"egulias/email-validator": "^3.2.5|^4.0",
"illuminate/collections": "^10.0",
"illuminate/container": "^10.0",
"illuminate/contracts": "^10.0",
"illuminate/macroable": "^10.0",
"illuminate/support": "^10.0",
"illuminate/translation": "^10.0",
"symfony/http-foundation": "^6.4",
"symfony/mime": "^6.2"
},
"autoload": {
"psr-4": {
"Illuminate\\Validation\\": ""
}
},
"extra": {
"branch-alias": {
"dev-master": "10.x-dev"
}
},
"suggest": {
"illuminate/database": "Required to use the database presence verifier (^10.0)."
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}