This commit is contained in:
Enoch
2024-08-09 22:16:39 +08:00
commit d78b38e80f
3984 changed files with 416946 additions and 0 deletions
@@ -0,0 +1,98 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Helpers\CanValidateDateTime;
use function date;
use function date_parse_from_format;
use function is_scalar;
use function strtotime;
use function vsprintf;
/**
* Abstract class to validate ages.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
abstract class AbstractAge extends AbstractRule
{
use CanValidateDateTime;
/**
* @var int
*/
private $age;
/**
* @var string|null
*/
private $format;
/**
* @var int
*/
private $baseDate;
/**
* Should compare the current base date with the given one.
*
* The dates are represented as integers in the format "Ymd".
*/
abstract protected function compare(int $baseDate, int $givenDate): bool;
/**
* Initializes the rule.
*/
public function __construct(int $age, ?string $format = null)
{
$this->age = $age;
$this->format = $format;
$this->baseDate = (int) date('Ymd') - $this->age * 10000;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_scalar($input)) {
return false;
}
if ($this->format === null) {
return $this->isValidWithoutFormat((string) $input);
}
return $this->isValidWithFormat($this->format, (string) $input);
}
private function isValidWithoutFormat(string $dateTime): bool
{
$timestamp = strtotime($dateTime);
if ($timestamp === false) {
return false;
}
return $this->compare($this->baseDate, (int) date('Ymd', $timestamp));
}
private function isValidWithFormat(string $format, string $dateTime): bool
{
if (!$this->isDateTime($format, $dateTime)) {
return false;
}
return $this->compare(
$this->baseDate,
(int) vsprintf('%d%02d%02d', date_parse_from_format($format, $dateTime))
);
}
}
@@ -0,0 +1,60 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Helpers\CanCompareValues;
/**
* Abstract class to help on creating rules that compare value.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
abstract class AbstractComparison extends AbstractRule
{
use CanCompareValues;
/**
* @var mixed
*/
private $compareTo;
/**
* Compare both values and return whether the comparison is valid or not.
*
* @param mixed $left
* @param mixed $right
*/
abstract protected function compare($left, $right): bool;
/**
* Initializes the rule by setting the value to be compared to the input.
*
* @param mixed $maxValue
*/
public function __construct($maxValue)
{
$this->compareTo = $maxValue;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
$left = $this->toComparable($input);
$right = $this->toComparable($this->compareTo);
if (!$this->isAbleToCompareValues($left, $right)) {
return false;
}
return $this->compare($left, $right);
}
}
@@ -0,0 +1,150 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validatable;
use function array_filter;
use function array_map;
/**
* Abstract class for rules that are composed by other rules.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Wojciech Frącz <fraczwojciech@gmail.com>
*/
abstract class AbstractComposite extends AbstractRule
{
/**
* @var Validatable[]
*/
private $rules = [];
/**
* Initializes the rule adding other rules to the stack.
*/
public function __construct(Validatable ...$rules)
{
$this->rules = $rules;
}
/**
* {@inheritDoc}
*/
public function setName(string $name): Validatable
{
$parentName = $this->getName();
foreach ($this->rules as $rule) {
$ruleName = $rule->getName();
if ($ruleName && $parentName !== $ruleName) {
continue;
}
$rule->setName($name);
}
return parent::setName($name);
}
public function setDefault(string $default, bool $defaultType=false): Validatable
{
$parentDefault = $this->getDefault();
foreach ($this->rules as $rule) {
$ruleDefault = $rule->getDefault();
if ($ruleDefault && $parentDefault !== $ruleDefault) {
continue;
}
$rule->setDefault($default, $defaultType);
}
return parent::setDefault($default, $defaultType);
}
/**
* Append a rule into the stack of rules.
*
* @return AbstractComposite
*/
public function addRule(Validatable $rule): self
{
if ($this->shouldHaveNameOverwritten($rule) && $this->getName() !== null) {
$rule->setName($this->getName());
}
$this->rules[] = $rule;
return $this;
}
/**
* Returns all the rules in the stack.
*
* @return Validatable[]
*/
public function getRules(): array
{
return $this->rules;
}
/**
* Returns all the exceptions throw when asserting all rules.
*
* @param mixed $input
*
* @return ValidationException[]
*/
protected function getAllThrownExceptions($input): array
{
return array_filter(
array_map(
function (Validatable $rule) use ($input): ?ValidationException {
try {
$rule->assert($input);
} catch (ValidationException $exception) {
$this->updateExceptionTemplate($exception);
return $exception;
}
return null;
},
$this->getRules()
)
);
}
private function shouldHaveNameOverwritten(Validatable $rule): bool
{
return $this->hasName($this) && !$this->hasName($rule);
}
private function hasName(Validatable $rule): bool
{
return $rule->getName() !== null;
}
private function updateExceptionTemplate(ValidationException $exception): void
{
if ($this->template === null || $exception->hasCustomTemplate()) {
return;
}
$exception->updateTemplate($this->template);
if (!$exception instanceof NestedValidationException) {
return;
}
foreach ($exception->getChildren() as $childException) {
$this->updateExceptionTemplate($childException);
}
}
}
@@ -0,0 +1,61 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validatable;
/**
* Abstract class that creates an envelope around another rule.
*
* This class is usefull when you want to create rules that use other rules, but
* having an custom message.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
abstract class AbstractEnvelope extends AbstractRule
{
/**
* @var Validatable
*/
private $validatable;
/**
* @var mixed[]
*/
private $parameters;
/**
* Initializes the rule.
*
* @param mixed[] $parameters
*/
public function __construct(Validatable $validatable, array $parameters = [])
{
$this->validatable = $validatable;
$this->parameters = $parameters;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return $this->validatable->validate($input);
}
/**
* {@inheritDoc}
*/
public function reportError($input, array $extraParameters = []): ValidationException
{
return parent::reportError($input, $extraParameters + $this->parameters);
}
}
@@ -0,0 +1,61 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function implode;
use function is_scalar;
use function str_replace;
use function str_split;
/**
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
*/
abstract class AbstractFilterRule extends AbstractRule
{
/**
* @var string
*/
private $additionalChars;
abstract protected function validateFilteredInput(string $input): bool;
/**
* Initializes the rule with a list of characters to be ignored by the validation.
*/
public function __construct(string ...$additionalChars)
{
$this->additionalChars = implode($additionalChars);
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_scalar($input)) {
return false;
}
$stringInput = (string) $input;
if ($stringInput === '') {
return false;
}
$filteredInput = $this->filter($stringInput);
return $filteredInput === '' || $this->validateFilteredInput($filteredInput);
}
private function filter(string $input): string
{
return str_replace(str_split($this->additionalChars), '', $input);
}
}
@@ -0,0 +1,154 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validatable;
use function is_scalar;
/**
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
*/
abstract class AbstractRelated extends AbstractRule
{
/**
* @var bool
*/
private $mandatory = true;
/**
* @var mixed
*/
private $reference;
/**
* @var Validatable|null
*/
private $rule;
/**
* @param mixed $input
*/
abstract public function hasReference($input): bool;
/**
* @param mixed $input
*
* @return mixed
*/
abstract public function getReferenceValue($input);
/**
* @param mixed $reference
*/
public function __construct($reference, ?Validatable $rule = null, bool $mandatory = true)
{
$this->reference = $reference;
$this->rule = $rule;
$this->mandatory = $mandatory;
if ($rule && $rule->getName() !== null) {
$this->setName($rule->getName());
} elseif (is_scalar($reference)) {
$this->setName((string) $reference);
}
}
/**
* @return mixed
*/
public function getReference()
{
return $this->reference;
}
public function isMandatory(): bool
{
return $this->mandatory;
}
/**
* {@inheritDoc}
*/
public function setName(string $name): Validatable
{
parent::setName($name);
if ($this->rule instanceof Validatable) {
$this->rule->setName($name);
}
return $this;
}
/**
* {@inheritDoc}
*/
public function assert($input): void
{
$hasReference = $this->hasReference($input);
if ($this->mandatory && !$hasReference) {
throw $this->reportError($input, ['hasReference' => false]);
}
if ($this->rule === null || !$hasReference) {
return;
}
try {
$this->rule->assert($this->getReferenceValue($input));
} catch (ValidationException $validationException) {
/** @var NestedValidationException $nestedValidationException */
$nestedValidationException = $this->reportError($this->reference, ['hasReference' => true]);
$nestedValidationException->addChild($validationException);
throw $nestedValidationException;
}
}
/**
* {@inheritDoc}
*/
public function check($input): void
{
$hasReference = $this->hasReference($input);
if ($this->mandatory && !$hasReference) {
throw $this->reportError($input, ['hasReference' => false]);
}
if ($this->rule === null || !$hasReference) {
return;
}
$this->rule->check($this->getReferenceValue($input));
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
$hasReference = $this->hasReference($input);
if ($this->mandatory && !$hasReference) {
return false;
}
if ($this->rule === null || !$hasReference) {
return true;
}
return $this->rule->validate($this->getReferenceValue($input));
}
}
@@ -0,0 +1,126 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Factory;
use Respect\Validation\Validatable;
/**
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
* @author Vicente Mendoza <vicentemmor@yahoo.com.mx>
*/
abstract class AbstractRule implements Validatable
{
/**
* @var string|null
*/
protected $default;
/**
* @var bool|false
*/
protected $defaultType;
/**
* @var string|null
*/
protected $name;
/**
* @var string|null
*/
protected $template;
/**
* {@inheritDoc}
*/
public function assert($input): void
{
if ($this->validate($input)) {
return;
}
throw $this->reportError($input);
}
/**
* {@inheritDoc}
*/
public function check($input): void
{
$this->assert($input);
}
/**
* {@inheritDoc}
*/
public function getName(): ?string
{
return $this->name;
}
/**
* {@inheritDoc}
*/
public function reportError($input, array $extraParams = []): ValidationException
{
return Factory::getDefaultInstance()->exception($this, $input, $extraParams);
}
/**
* {@inheritDoc}
*/
public function setName(string $name): Validatable
{
$this->name = $name;
return $this;
}
/**
* {@inheritDoc}
*/
public function setTemplate(string $template): Validatable
{
$this->template = $template;
return $this;
}
/**
* {@inheritDoc}
*/
public function getDefault(): ?string
{
return $this->default;
}
/**
* {@inheritDoc}
*/
public function getDefaultType(): ?bool
{
return $this->defaultType;
}
public function setDefault(string $default, bool $defaultType=false): Validatable
{
$this->default = $default;
$this->defaultType = $defaultType;
return $this;
}
/**
* @param mixed$input
*/
public function __invoke($input): bool
{
return $this->validate($input);
}
}
@@ -0,0 +1,50 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Helpers\CanValidateUndefined;
use function in_array;
use function is_scalar;
use function mb_strtoupper;
/**
* Abstract class for searches into arrays.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
abstract class AbstractSearcher extends AbstractRule
{
use CanValidateUndefined;
/**
* @param mixed $input
* @return mixed[]
*/
abstract protected function getDataSource($input = null): array;
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
$dataSource = $this->getDataSource($input);
if ($this->isUndefined($input) && empty($dataSource)) {
return true;
}
if (!is_scalar($input)) {
return false;
}
return in_array(mb_strtoupper((string) $input), $dataSource, true);
}
}
@@ -0,0 +1,68 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Validatable;
/**
* Abstract class to help on creating rules that wrap rules.
*
* @author Alasdair North <alasdair@runway.io>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
abstract class AbstractWrapper extends AbstractRule
{
/**
* @var Validatable
*/
private $validatable;
/**
* Initializes the rule.
*/
public function __construct(Validatable $validatable)
{
$this->validatable = $validatable;
}
/**
* {@inheritDoc}
*/
public function assert($input): void
{
$this->validatable->assert($input);
}
/**
* {@inheritDoc}
*/
public function check($input): void
{
$this->validatable->check($input);
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return $this->validatable->validate($input);
}
/**
* {@inheritDoc}
*/
public function setName(string $name): Validatable
{
$this->validatable->setName($name);
return parent::setName($name);
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\AllOfException;
use function count;
/**
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
class AllOf extends AbstractComposite
{
/**
* {@inheritDoc}
*/
public function assert($input): void
{
$exceptions = $this->getAllThrownExceptions($input);
$numRules = count($this->getRules());
$numExceptions = count($exceptions);
$summary = [
'total' => $numRules,
'failed' => $numExceptions,
'passed' => $numRules - $numExceptions,
];
if (!empty($exceptions)) {
/** @var AllOfException $allOfException */
$allOfException = $this->reportError($input, $summary);
$allOfException->addChildren($exceptions);
throw $allOfException;
}
}
/**
* {@inheritDoc}
*/
public function check($input): void
{
foreach ($this->getRules() as $rule) {
$rule->check($input);
}
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
foreach ($this->getRules() as $rule) {
if (!$rule->validate($input)) {
return false;
}
}
return true;
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function ctype_alnum;
/**
* Validates whether the input is alphanumeric or not.
*
* Alphanumeric is a combination of alphabetic (a-z and A-Z) and numeric (0-9)
* characters.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
*/
final class Alnum extends AbstractFilterRule
{
/**
* {@inheritDoc}
*/
protected function validateFilteredInput(string $input): bool
{
return ctype_alnum($input);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function ctype_alpha;
/**
* Validates whether the input contains only alphabetic characters.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
*/
final class Alpha extends AbstractFilterRule
{
/**
* {@inheritDoc}
*/
protected function validateFilteredInput(string $input): bool
{
return ctype_alpha($input);
}
}
@@ -0,0 +1,28 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates any input as invalid.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class AlwaysInvalid extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return false;
}
}
@@ -0,0 +1,28 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates any input as valid.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class AlwaysValid extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return true;
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\AnyOfException;
use Respect\Validation\Exceptions\ValidationException;
use function count;
/**
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class AnyOf extends AbstractComposite
{
/**
* {@inheritDoc}
*/
public function assert($input): void
{
$validators = $this->getRules();
$exceptions = $this->getAllThrownExceptions($input);
$numRules = count($validators);
$numExceptions = count($exceptions);
if ($numExceptions === $numRules) {
/** @var AnyOfException $anyOfException */
$anyOfException = $this->reportError($input);
$anyOfException->addChildren($exceptions);
throw $anyOfException;
}
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
foreach ($this->getRules() as $v) {
if ($v->validate($input)) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
public function check($input): void
{
foreach ($this->getRules() as $v) {
try {
$v->check($input);
return;
} catch (ValidationException $e) {
if (!isset($firstException)) {
$firstException = $e;
}
}
}
if (isset($firstException)) {
throw $firstException;
}
throw $this->reportError($input);
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_array;
/**
* Validates whether the type of an input is array.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author João Torquato <joao.otl@gmail.com>
*/
final class ArrayType extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_array($input);
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use ArrayAccess;
use SimpleXMLElement;
use function is_array;
/**
* Validates if the input is an array or if the input can be used as an array.
*
* Instance of `ArrayAccess` or `SimpleXMLElement` are also considered as valid.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class ArrayVal extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_array($input) || $input instanceof ArrayAccess || $input instanceof SimpleXMLElement;
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use ReflectionException;
use ReflectionProperty;
use Respect\Validation\Validatable;
use function is_object;
use function property_exists;
/**
* Validates an object attribute, event private ones.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Attribute extends AbstractRelated
{
public function __construct(string $reference, ?Validatable $rule = null, bool $mandatory = true)
{
parent::__construct($reference, $rule, $mandatory);
}
/**
* {@inheritDoc}
*
* @throws ReflectionException
*/
public function getReferenceValue($input)
{
$propertyMirror = new ReflectionProperty($input, (string) $this->getReference());
$propertyMirror->setAccessible(true);
return $propertyMirror->getValue($input);
}
/**
* {@inheritDoc}
*/
public function hasReference($input): bool
{
return is_object($input) && property_exists($input, (string) $this->getReference());
}
}
+64
View File
@@ -0,0 +1,64 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use function is_null;
use function mb_strlen;
use function mb_substr;
use function preg_match;
use function sprintf;
/**
* Validate numbers in any base, even with non regular bases.
*
* @author Carlos André Ferrari <caferrari@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Base extends AbstractRule
{
/**
* @var string
*/
private $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
/**
* @var int
*/
private $base;
/**
* Initializes the Base rule.
*/
public function __construct(int $base, ?string $chars = null)
{
if (!is_null($chars)) {
$this->chars = $chars;
}
$max = mb_strlen($this->chars);
if ($base > $max) {
throw new ComponentException(sprintf('a base between 1 and %s is required', $max));
}
$this->base = $base;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
$valid = mb_substr($this->chars, 0, $this->base);
return (bool) preg_match('@^[' . $valid . ']+$@', (string) $input);
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_string;
use function mb_strlen;
use function preg_match;
/**
* Validate if a string is Base64-encoded.
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jens Segers <segers.jens@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Base64 extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_string($input)) {
return false;
}
if (!preg_match('#^[A-Za-z0-9+/\n\r]+={0,2}$#', $input)) {
return false;
}
return mb_strlen($input) % 4 === 0;
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Helpers\CanCompareValues;
/**
* Validates whether the input is between two other values.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Between extends AbstractEnvelope
{
use CanCompareValues;
/**
* Initializes the rule.
*
* @param mixed $minValue
* @param mixed $maxValue
*
* @throws ComponentException
*/
public function __construct($minValue, $maxValue)
{
if ($this->toComparable($minValue) >= $this->toComparable($maxValue)) {
throw new ComponentException('Minimum cannot be less than or equals to maximum');
}
parent::__construct(
new AllOf(
new Min($minValue),
new Max($maxValue)
),
[
'minValue' => $minValue,
'maxValue' => $maxValue,
]
);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_bool;
/**
* Validates whether the type of the input is boolean.
*
* @author Devin Torres <devin@devintorres.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class BoolType extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_bool($input);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function filter_var;
use function is_bool;
use const FILTER_NULL_ON_FAILURE;
use const FILTER_VALIDATE_BOOLEAN;
/**
* Validates if the input results in a boolean value.
*
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class BoolVal extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_bool(filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE));
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function ctype_digit;
use function intval;
use function is_scalar;
use function mb_strlen;
use function strval;
/**
* Validates a Dutch citizen service number (BSN).
*
* @see https://nl.wikipedia.org/wiki/Burgerservicenummer
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Ronald Drenth <ronalddrenth@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Bsn extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_scalar($input)) {
return false;
}
$input = (string) $input;
if (!ctype_digit($input)) {
return false;
}
if (mb_strlen(strval($input)) !== 9) {
return false;
}
$sum = -1 * intval($input[8]);
for ($i = 9; $i > 1; --$i) {
$sum += $i * intval($input[9 - $i]);
}
return $sum !== 0 && $sum % 11 === 0;
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validatable;
use Throwable;
use function call_user_func;
use function restore_error_handler;
use function set_error_handler;
/**
* Validates the return of a callable for a given input.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Call extends AbstractRule
{
/**
* @var callable
*/
private $callable;
/**
* @var Validatable
*/
private $rule;
/**
* Initializes the rule with the callable to be executed after the input is passed.
*/
public function __construct(callable $callable, Validatable $rule)
{
$this->callable = $callable;
$this->rule = $rule;
}
/**
* {@inheritDoc}
*/
public function assert($input): void
{
$this->setErrorHandler($input);
try {
$this->rule->assert(call_user_func($this->callable, $input));
} catch (ValidationException $exception) {
throw $exception;
} catch (Throwable $throwable) {
throw $this->reportError($input);
} finally {
restore_error_handler();
}
}
/**
* {@inheritDoc}
*/
public function check($input): void
{
$this->setErrorHandler($input);
try {
$this->rule->check(call_user_func($this->callable, $input));
} catch (ValidationException $exception) {
throw $exception;
} catch (Throwable $throwable) {
throw $this->reportError($input);
} finally {
restore_error_handler();
}
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
try {
$this->check($input);
} catch (ValidationException $exception) {
return false;
}
return true;
}
/**
* @param mixed $input
*/
private function setErrorHandler($input): void
{
set_error_handler(function () use ($input): void {
throw $this->reportError($input);
});
}
}
@@ -0,0 +1,28 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_callable;
/**
* Validates whether the pseudo-type of the input is callable.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class CallableType extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_callable($input);
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function array_merge;
use function call_user_func_array;
use function count;
/**
* Validates the input using the return of a given callable.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Callback extends AbstractRule
{
/**
* @var callable
*/
private $callback;
/**
* @var mixed[]
*/
private $arguments;
/**
* Initializes the rule.
*
* @param mixed ...$arguments
*/
public function __construct(callable $callback, ...$arguments)
{
$this->callback = $callback;
$this->arguments = $arguments;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return (bool) call_user_func_array($this->callback, $this->getArguments($input));
}
/**
* @param mixed $input
* @return mixed[]
*/
private function getArguments($input): array
{
$arguments = [$input];
if (count($this->arguments) === 0) {
return $arguments;
}
return array_merge($arguments, $this->arguments);
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use function array_diff;
use function in_array;
use function mb_detect_encoding;
use function mb_list_encodings;
/**
* Validates if a string is in a specific charset.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Charset extends AbstractRule
{
/**
* @var string[]
*/
private $charset;
/**
* Initializes the rule.
*
* @throws ComponentException
*/
public function __construct(string ...$charset)
{
$available = mb_list_encodings();
if (!empty(array_diff($charset, $available))) {
throw new ComponentException('Invalid charset');
}
$this->charset = $charset;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return in_array(mb_detect_encoding($input, $this->charset, true), $this->charset, true);
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_scalar;
use function mb_strlen;
use function preg_replace;
/**
* Validates a Brazilian driver's license.
*
* @author Gabriel Pedro <gpedro@users.noreply.github.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Kinn Coelho Julião <kinncj@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Cnh extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_scalar($input)) {
return false;
}
// Canonicalize input
$input = (string) preg_replace('{\D}', '', (string) $input);
// Validate length and invalid numbers
if (mb_strlen($input) != 11 || ((int) $input === 0)) {
return false;
}
// Validate check digits using a modulus 11 algorithm
for ($c = $s1 = $s2 = 0, $p = 9; $c < 9; $c++, $p--) {
$s1 += (int) $input[$c] * $p;
$s2 += (int) $input[$c] * (10 - $p);
}
$dv1 = $s1 % 11;
if ($input[9] != ($dv1 > 9) ? 0 : $dv1) {
return false;
}
$dv2 = $s2 % 11 - ($dv1 > 9 ? 2 : 0);
$check = $dv2 < 0 ? $dv2 + 11 : ($dv2 > 9 ? 0 : $dv2);
return $input[10] == $check;
}
}
+83
View File
@@ -0,0 +1,83 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function array_map;
use function array_sum;
use function count;
use function is_scalar;
use function preg_replace;
use function str_split;
/**
* Validates if the input is a Brazilian National Registry of Legal Entities (CNPJ) number.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jayson Reis <santosdosreis@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
* @author Renato Moura <renato@naturalweb.com.br>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Cnpj extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_scalar($input)) {
return false;
}
// Code ported from jsfromhell.com
$bases = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
$digits = $this->getDigits((string) $input);
if (array_sum($digits) < 1) {
return false;
}
if (count($digits) !== 14) {
return false;
}
$n = 0;
for ($i = 0; $i < 12; ++$i) {
$n += $digits[$i] * $bases[$i + 1];
}
if ($digits[12] != (($n %= 11) < 2 ? 0 : 11 - $n)) {
return false;
}
$n = 0;
for ($i = 0; $i <= 12; ++$i) {
$n += $digits[$i] * $bases[$i];
}
$check = ($n %= 11) < 2 ? 0 : 11 - $n;
return $digits[13] == $check;
}
/**
* @return int[]
*/
private function getDigits(string $input): array
{
return array_map(
'intval',
str_split(
(string) preg_replace('/\D/', '', $input)
)
);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function preg_match;
/**
* Validates if the input contains only consonants.
*
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
*/
final class Consonant extends AbstractFilterRule
{
/**
* {@inheritDoc}
*/
protected function validateFilteredInput(string $input): bool
{
return preg_match('/^(\s|[b-df-hj-np-tv-zB-DF-HJ-NP-TV-Z])*$/', $input) > 0;
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function in_array;
use function is_array;
use function is_scalar;
use function mb_stripos;
use function mb_strpos;
/**
* Validates if the input contains some value.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Marcelo Araujo <msaraujo@php.net>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Contains extends AbstractRule
{
/**
* @var mixed
*/
private $containsValue;
/**
* @var bool
*/
private $identical;
/**
* Initializes the Contains rule.
*
* @param mixed $containsValue Value that will be sought
* @param bool $identical Defines whether the value is identical, default is false
*/
public function __construct($containsValue, bool $identical = false)
{
$this->containsValue = $containsValue;
$this->identical = $identical;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (is_array($input)) {
return in_array($this->containsValue, $input, $this->identical);
}
if (!is_scalar($input) || !is_scalar($this->containsValue)) {
return false;
}
return $this->validateString((string) $input, (string) $this->containsValue);
}
private function validateString(string $haystack, string $needle): bool
{
if ($needle === '') {
return false;
}
if ($this->identical) {
return mb_strpos($haystack, $needle) !== false;
}
return mb_stripos($haystack, $needle) !== false;
}
}
@@ -0,0 +1,50 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function array_map;
/**
* Validates if the input contains at least one of defined values
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Kirill Dlussky <kirill@dlussky.ru>
*/
final class ContainsAny extends AbstractEnvelope
{
/**
* Initializes the rule.
*
* @param mixed[] $needles At least one of the values provided must be found in input string or array
* @param bool $identical Defines whether the value should be compared strictly, when validating array
*/
public function __construct(array $needles, bool $identical = false)
{
parent::__construct(
new AnyOf(...$this->getRules($needles, $identical)),
['needles' => $needles]
);
}
/**
* @param mixed[] $needles
*
* @return Contains[]
*/
private function getRules(array $needles, bool $identical): array
{
return array_map(
static function ($needle) use ($identical): Contains {
return new Contains($needle, $identical);
},
$needles
);
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function ctype_cntrl;
/**
* Validates if all of the characters in the provided string, are control characters.
*
* @author Andre Ramaciotti <andre@ramaciotti.com>
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
*/
final class Control extends AbstractFilterRule
{
/**
* {@inheritDoc}
*/
protected function validateFilteredInput(string $input): bool
{
return ctype_cntrl($input);
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Countable as CountableInterface;
use function is_array;
/**
* Validates if the input is countable.
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author João Torquato <joao.otl@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Countable extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_array($input) || $input instanceof CountableInterface;
}
}
@@ -0,0 +1,375 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use function array_column;
use function array_keys;
use function implode;
use function sprintf;
/**
* Validates whether the input is a country code in ISO 3166-1 standard.
*
* This rule supports the three sets of country codes (alpha-2, alpha-3, and numeric).
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Felipe Martins <me@fefas.net>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class CountryCode extends AbstractSearcher
{
/**
* The ISO representation of a country code.
*/
public const ALPHA2 = 'alpha-2';
/**
* The ISO3 representation of a country code.
*/
public const ALPHA3 = 'alpha-3';
/**
* The ISO-number representation of a country code.
*/
public const NUMERIC = 'numeric';
/**
* Position of the indexes of each set in the list of country codes.
*/
private const SET_INDEXES = [
self::ALPHA2 => 0,
self::ALPHA3 => 1,
self::NUMERIC => 2,
];
/**
* @see https://salsa.debian.org/iso-codes-team/iso-codes
*/
private const COUNTRY_CODES = [
// begin of auto-generated code
['AD', 'AND', '020'], // Andorra
['AE', 'ARE', '784'], // United Arab Emirates
['AF', 'AFG', '004'], // Afghanistan
['AG', 'ATG', '028'], // Antigua and Barbuda
['AI', 'AFI', '262'], // French Afars and Issas
['AI', 'AIA', '660'], // Anguilla
['AL', 'ALB', '008'], // Albania
['AM', 'ARM', '051'], // Armenia
['AN', 'ANT', '530'], // Netherlands Antilles
['AO', 'AGO', '024'], // Angola
['AQ', 'ATA', '010'], // Antarctica
['AR', 'ARG', '032'], // Argentina
['AS', 'ASM', '016'], // American Samoa
['AT', 'AUT', '040'], // Austria
['AU', 'AUS', '036'], // Australia
['AW', 'ABW', '533'], // Aruba
['AX', 'ALA', '248'], // Åland Islands
['AZ', 'AZE', '031'], // Azerbaijan
['BA', 'BIH', '070'], // Bosnia and Herzegovina
['BB', 'BRB', '052'], // Barbados
['BD', 'BGD', '050'], // Bangladesh
['BE', 'BEL', '056'], // Belgium
['BF', 'BFA', '854'], // Burkina Faso
['BG', 'BGR', '100'], // Bulgaria
['BH', 'BHR', '048'], // Bahrain
['BI', 'BDI', '108'], // Burundi
['BJ', 'BEN', '204'], // Benin
['BL', 'BLM', '652'], // Saint Barthélemy
['BM', 'BMU', '060'], // Bermuda
['BN', 'BRN', '096'], // Brunei Darussalam
['BO', 'BOL', '068'], // Bolivia, Plurinational State of
['BQ', 'ATB', null], // British Antarctic Territory
['BQ', 'BES', '535'], // Bonaire, Sint Eustatius and Saba
['BR', 'BRA', '076'], // Brazil
['BS', 'BHS', '044'], // Bahamas
['BT', 'BTN', '064'], // Bhutan
['BU', 'BUR', '104'], // Burma, Socialist Republic of the Union of
['BV', 'BVT', '074'], // Bouvet Island
['BW', 'BWA', '072'], // Botswana
['BY', 'BLR', '112'], // Belarus
['BY', 'BYS', '112'], // Byelorussian SSR Soviet Socialist Republic
['BZ', 'BLZ', '084'], // Belize
['CA', 'CAN', '124'], // Canada
['CC', 'CCK', '166'], // Cocos (Keeling) Islands
['CD', 'COD', '180'], // Congo, The Democratic Republic of the
['CF', 'CAF', '140'], // Central African Republic
['CG', 'COG', '178'], // Congo
['CH', 'CHE', '756'], // Switzerland
['CI', 'CIV', '384'], // Côte d'Ivoire
['CK', 'COK', '184'], // Cook Islands
['CL', 'CHL', '152'], // Chile
['CM', 'CMR', '120'], // Cameroon
['CN', 'CHN', '156'], // China
['CO', 'COL', '170'], // Colombia
['CR', 'CRI', '188'], // Costa Rica
['CS', 'CSK', '200'], // Czechoslovakia, Czechoslovak Socialist Republic
['CS', 'SCG', '891'], // Serbia and Montenegro
['CT', 'CTE', '128'], // Canton and Enderbury Islands
['CU', 'CUB', '192'], // Cuba
['CV', 'CPV', '132'], // Cabo Verde
['CW', 'CUW', '531'], // Curaçao
['CX', 'CXR', '162'], // Christmas Island
['CY', 'CYP', '196'], // Cyprus
['CZ', 'CZE', '203'], // Czechia
['DD', 'DDR', '278'], // German Democratic Republic
['DE', 'DEU', '276'], // Germany
['DJ', 'DJI', '262'], // Djibouti
['DK', 'DNK', '208'], // Denmark
['DM', 'DMA', '212'], // Dominica
['DO', 'DOM', '214'], // Dominican Republic
['DY', 'DHY', '204'], // Dahomey
['DZ', 'DZA', '012'], // Algeria
['EC', 'ECU', '218'], // Ecuador
['EE', 'EST', '233'], // Estonia
['EG', 'EGY', '818'], // Egypt
['EH', 'ESH', '732'], // Western Sahara
['ER', 'ERI', '232'], // Eritrea
['ES', 'ESP', '724'], // Spain
['ET', 'ETH', '231'], // Ethiopia
['FI', 'FIN', '246'], // Finland
['FJ', 'FJI', '242'], // Fiji
['FK', 'FLK', '238'], // Falkland Islands (Malvinas)
['FM', 'FSM', '583'], // Micronesia, Federated States of
['FO', 'FRO', '234'], // Faroe Islands
['FQ', 'ATF', null], // French Southern and Antarctic Territories
['FR', 'FRA', '250'], // France
['FX', 'FXX', '249'], // France, Metropolitan
['GA', 'GAB', '266'], // Gabon
['GB', 'GBR', '826'], // United Kingdom
['GD', 'GRD', '308'], // Grenada
['GE', 'GEL', '296'], // Gilbert and Ellice Islands
['GE', 'GEO', '268'], // Georgia
['GF', 'GUF', '254'], // French Guiana
['GG', 'GGY', '831'], // Guernsey
['GH', 'GHA', '288'], // Ghana
['GI', 'GIB', '292'], // Gibraltar
['GL', 'GRL', '304'], // Greenland
['GM', 'GMB', '270'], // Gambia
['GN', 'GIN', '324'], // Guinea
['GP', 'GLP', '312'], // Guadeloupe
['GQ', 'GNQ', '226'], // Equatorial Guinea
['GR', 'GRC', '300'], // Greece
['GS', 'SGS', '239'], // South Georgia and the South Sandwich Islands
['GT', 'GTM', '320'], // Guatemala
['GU', 'GUM', '316'], // Guam
['GW', 'GNB', '624'], // Guinea-Bissau
['GY', 'GUY', '328'], // Guyana
['HK', 'HKG', '344'], // Hong Kong
['HM', 'HMD', '334'], // Heard Island and McDonald Islands
['HN', 'HND', '340'], // Honduras
['HR', 'HRV', '191'], // Croatia
['HT', 'HTI', '332'], // Haiti
['HU', 'HUN', '348'], // Hungary
['HV', 'HVO', '854'], // Upper Volta, Republic of
['ID', 'IDN', '360'], // Indonesia
['IE', 'IRL', '372'], // Ireland
['IL', 'ISR', '376'], // Israel
['IM', 'IMN', '833'], // Isle of Man
['IN', 'IND', '356'], // India
['IO', 'IOT', '086'], // British Indian Ocean Territory
['IQ', 'IRQ', '368'], // Iraq
['IR', 'IRN', '364'], // Iran, Islamic Republic of
['IS', 'ISL', '352'], // Iceland
['IT', 'ITA', '380'], // Italy
['JE', 'JEY', '832'], // Jersey
['JM', 'JAM', '388'], // Jamaica
['JO', 'JOR', '400'], // Jordan
['JP', 'JPN', '392'], // Japan
['JT', 'JTN', '396'], // Johnston Island
['KE', 'KEN', '404'], // Kenya
['KG', 'KGZ', '417'], // Kyrgyzstan
['KH', 'KHM', '116'], // Cambodia
['KI', 'KIR', '296'], // Kiribati
['KM', 'COM', '174'], // Comoros
['KN', 'KNA', '659'], // Saint Kitts and Nevis
['KP', 'PRK', '408'], // Korea, Democratic People's Republic of
['KR', 'KOR', '410'], // Korea, Republic of
['KW', 'KWT', '414'], // Kuwait
['KY', 'CYM', '136'], // Cayman Islands
['KZ', 'KAZ', '398'], // Kazakhstan
['LA', 'LAO', '418'], // Lao People's Democratic Republic
['LB', 'LBN', '422'], // Lebanon
['LC', 'LCA', '662'], // Saint Lucia
['LI', 'LIE', '438'], // Liechtenstein
['LK', 'LKA', '144'], // Sri Lanka
['LR', 'LBR', '430'], // Liberia
['LS', 'LSO', '426'], // Lesotho
['LT', 'LTU', '440'], // Lithuania
['LU', 'LUX', '442'], // Luxembourg
['LV', 'LVA', '428'], // Latvia
['LY', 'LBY', '434'], // Libya
['MA', 'MAR', '504'], // Morocco
['MC', 'MCO', '492'], // Monaco
['MD', 'MDA', '498'], // Moldova, Republic of
['ME', 'MNE', '499'], // Montenegro
['MF', 'MAF', '663'], // Saint Martin (French part)
['MG', 'MDG', '450'], // Madagascar
['MH', 'MHL', '584'], // Marshall Islands
['MI', 'MID', '488'], // Midway Islands
['MK', 'MKD', '807'], // North Macedonia
['ML', 'MLI', '466'], // Mali
['MM', 'MMR', '104'], // Myanmar
['MN', 'MNG', '496'], // Mongolia
['MO', 'MAC', '446'], // Macao
['MP', 'MNP', '580'], // Northern Mariana Islands
['MQ', 'MTQ', '474'], // Martinique
['MR', 'MRT', '478'], // Mauritania
['MS', 'MSR', '500'], // Montserrat
['MT', 'MLT', '470'], // Malta
['MU', 'MUS', '480'], // Mauritius
['MV', 'MDV', '462'], // Maldives
['MW', 'MWI', '454'], // Malawi
['MX', 'MEX', '484'], // Mexico
['MY', 'MYS', '458'], // Malaysia
['MZ', 'MOZ', '508'], // Mozambique
['NA', 'NAM', '516'], // Namibia
['NC', 'NCL', '540'], // New Caledonia
['NE', 'NER', '562'], // Niger
['NF', 'NFK', '574'], // Norfolk Island
['NG', 'NGA', '566'], // Nigeria
['NH', 'NHB', '548'], // New Hebrides
['NI', 'NIC', '558'], // Nicaragua
['NL', 'NLD', '528'], // Netherlands
['NO', 'NOR', '578'], // Norway
['NP', 'NPL', '524'], // Nepal
['NQ', 'ATN', '216'], // Dronning Maud Land
['NR', 'NRU', '520'], // Nauru
['NT', 'NTZ', '536'], // Neutral Zone
['NU', 'NIU', '570'], // Niue
['NZ', 'NZL', '554'], // New Zealand
['OM', 'OMN', '512'], // Oman
['PA', 'PAN', '591'], // Panama
['PC', 'PCI', '582'], // Pacific Islands (trust territory)
['PE', 'PER', '604'], // Peru
['PF', 'PYF', '258'], // French Polynesia
['PG', 'PNG', '598'], // Papua New Guinea
['PH', 'PHL', '608'], // Philippines
['PK', 'PAK', '586'], // Pakistan
['PL', 'POL', '616'], // Poland
['PM', 'SPM', '666'], // Saint Pierre and Miquelon
['PN', 'PCN', '612'], // Pitcairn
['PR', 'PRI', '630'], // Puerto Rico
['PS', 'PSE', '275'], // Palestine, State of
['PT', 'PRT', '620'], // Portugal
['PU', 'PUS', '849'], // US Miscellaneous Pacific Islands
['PW', 'PLW', '585'], // Palau
['PY', 'PRY', '600'], // Paraguay
['PZ', 'PCZ', null], // Panama Canal Zone
['QA', 'QAT', '634'], // Qatar
['RE', 'REU', '638'], // Réunion
['RH', 'RHO', '716'], // Southern Rhodesia
['RO', 'ROU', '642'], // Romania
['RS', 'SRB', '688'], // Serbia
['RU', 'RUS', '643'], // Russian Federation
['RW', 'RWA', '646'], // Rwanda
['SA', 'SAU', '682'], // Saudi Arabia
['SB', 'SLB', '090'], // Solomon Islands
['SC', 'SYC', '690'], // Seychelles
['SD', 'SDN', '729'], // Sudan
['SE', 'SWE', '752'], // Sweden
['SG', 'SGP', '702'], // Singapore
['SH', 'SHN', '654'], // Saint Helena, Ascension and Tristan da Cunha
['SI', 'SVN', '705'], // Slovenia
['SJ', 'SJM', '744'], // Svalbard and Jan Mayen
['SK', 'SKM', null], // Sikkim
['SK', 'SVK', '703'], // Slovakia
['SL', 'SLE', '694'], // Sierra Leone
['SM', 'SMR', '674'], // San Marino
['SN', 'SEN', '686'], // Senegal
['SO', 'SOM', '706'], // Somalia
['SR', 'SUR', '740'], // Suriname
['SS', 'SSD', '728'], // South Sudan
['ST', 'STP', '678'], // Sao Tome and Principe
['SU', 'SUN', '810'], // USSR, Union of Soviet Socialist Republics
['SV', 'SLV', '222'], // El Salvador
['SX', 'SXM', '534'], // Sint Maarten (Dutch part)
['SY', 'SYR', '760'], // Syrian Arab Republic
['SZ', 'SWZ', '748'], // Eswatini
['TC', 'TCA', '796'], // Turks and Caicos Islands
['TD', 'TCD', '148'], // Chad
['TF', 'ATF', '260'], // French Southern Territories
['TG', 'TGO', '768'], // Togo
['TH', 'THA', '764'], // Thailand
['TJ', 'TJK', '762'], // Tajikistan
['TK', 'TKL', '772'], // Tokelau
['TL', 'TLS', '626'], // Timor-Leste
['TM', 'TKM', '795'], // Turkmenistan
['TN', 'TUN', '788'], // Tunisia
['TO', 'TON', '776'], // Tonga
['TP', 'TMP', '626'], // East Timor
['TR', 'TUR', '792'], // Türkiye
['TT', 'TTO', '780'], // Trinidad and Tobago
['TV', 'TUV', '798'], // Tuvalu
['TW', 'TWN', '158'], // Taiwan, Province of China
['TZ', 'TZA', '834'], // Tanzania, United Republic of
['UA', 'UKR', '804'], // Ukraine
['UG', 'UGA', '800'], // Uganda
['UM', 'UMI', '581'], // United States Minor Outlying Islands
['US', 'USA', '840'], // United States
['UY', 'URY', '858'], // Uruguay
['UZ', 'UZB', '860'], // Uzbekistan
['VA', 'VAT', '336'], // Holy See (Vatican City State)
['VC', 'VCT', '670'], // Saint Vincent and the Grenadines
['VD', 'VDR', null], // Viet-Nam, Democratic Republic of
['VE', 'VEN', '862'], // Venezuela, Bolivarian Republic of
['VG', 'VGB', '092'], // Virgin Islands, British
['VI', 'VIR', '850'], // Virgin Islands, U.S.
['VN', 'VNM', '704'], // Viet Nam
['VU', 'VUT', '548'], // Vanuatu
['WF', 'WLF', '876'], // Wallis and Futuna
['WK', 'WAK', '872'], // Wake Island
['WS', 'WSM', '882'], // Samoa
['YD', 'YMD', '720'], // Yemen, Democratic, People's Democratic Republic of
['YE', 'YEM', '887'], // Yemen
['YT', 'MYT', '175'], // Mayotte
['YU', 'YUG', '891'], // Yugoslavia, Socialist Federal Republic of
['ZA', 'ZAF', '710'], // South Africa
['ZM', 'ZMB', '894'], // Zambia
['ZR', 'ZAR', '180'], // Zaire, Republic of
['ZW', 'ZWE', '716'], // Zimbabwe
// end of auto-generated code
];
/**
* @var string
*/
private $set;
/**
* Initializes the rule.
*
* @throws ComponentException If $set is not a valid set
*/
public function __construct(string $set = self::ALPHA2)
{
if (!isset(self::SET_INDEXES[$set])) {
throw new ComponentException(
sprintf(
'"%s" is not a valid set for ISO 3166-1 (Available: %s)',
$set,
implode(', ', array_keys(self::SET_INDEXES))
)
);
}
$this->set = $set;
}
/**
* {@inheritDoc}
*/
protected function getDataSource($input = null): array
{
return array_column(self::COUNTRY_CODES, self::SET_INDEXES[$this->set]);
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function intval;
use function mb_strlen;
use function preg_match;
use function preg_replace;
/**
* Validates whether the input is a CPF (Brazilian Natural Persons Register) number.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jair Henrique <jair.henrique@gmail.com>
* @author Jayson Reis <santosdosreis@gmail.com>
* @author Jean Pimentel <jeanfap@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Cpf extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
// Code ported from jsfromhell.com
$c = preg_replace('/\D/', '', $input);
if (mb_strlen($c) != 11 || preg_match('/^' . $c[0] . '{11}$/', $c) || $c === '01234567890') {
return false;
}
$n = 0;
for ($s = 10, $i = 0; $s >= 2; ++$i, --$s) {
$n += intval($c[$i]) * $s;
}
if ($c[9] != (($n %= 11) < 2 ? 0 : 11 - $n)) {
return false;
}
$n = 0;
for ($s = 11, $i = 0; $s >= 2; ++$i, --$s) {
$n += intval($c[$i]) * $s;
}
$check = ($n %= 11) < 2 ? 0 : 11 - $n;
return $c[10] == $check;
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use function array_keys;
use function implode;
use function is_scalar;
use function preg_match;
use function preg_replace;
use function sprintf;
/**
* Validates whether the input is a credit card number.
*
* @author Alexander Gorshkov <mazanax@yandex.ru>
* @author Andy Snell <andysnell@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jean Pimentel <jeanfap@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
* @author William Espindola <oi@williamespindola.com.br>
* @author Rakshit Arora <rakshit087@gmail.com>
*/
final class CreditCard extends AbstractRule
{
public const ANY = 'Any';
public const AMERICAN_EXPRESS = 'American Express';
public const DINERS_CLUB = 'Diners Club';
public const DISCOVER = 'Discover';
public const JCB = 'JCB';
public const MASTERCARD = 'MasterCard';
public const VISA = 'Visa';
public const RUPAY = 'RuPay';
private const BRAND_REGEX_LIST = [
self::ANY => '/^[0-9]+$/',
self::AMERICAN_EXPRESS => '/^3[47]\d{13}$/',
self::DINERS_CLUB => '/^3(?:0[0-5]|[68]\d)\d{11}$/',
self::DISCOVER => '/^6(?:011|5\d{2})\d{12}$/',
self::JCB => '/^(?:2131|1800|35\d{3})\d{11}$/',
self::MASTERCARD => '/(5[1-5]|2[2-7])\d{14}$/',
self::VISA => '/^4\d{12}(?:\d{3})?$/',
self::RUPAY => '/^6(?!011)(?:0[0-9]{14}|52[12][0-9]{12})$/',
];
/**
* @var string
*/
private $brand;
/**
* Initializes the rule.
*
* @throws ComponentException
*/
public function __construct(string $brand = self::ANY)
{
if (!isset(self::BRAND_REGEX_LIST[$brand])) {
throw new ComponentException(
sprintf(
'"%s" is not a valid credit card brand (Available: %s)',
$brand,
implode(', ', array_keys(self::BRAND_REGEX_LIST))
)
);
}
$this->brand = $brand;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_scalar($input)) {
return false;
}
$input = (string) preg_replace('/[ .-]/', '', (string) $input);
if (!(new Luhn())->validate($input)) {
return false;
}
return preg_match(self::BRAND_REGEX_LIST[$this->brand], $input) > 0;
}
}
@@ -0,0 +1,212 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates currency codes in ISO 4217.
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Justin Hook <justinhook88@yahoo.co.uk>
* @author Tim Strijdhorst <tstrijdhorst@users.noreply.github.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class CurrencyCode extends AbstractSearcher
{
/**
* @see http://www.currency-iso.org/en/home/tables/table-a1.html
*
* {@inheritDoc}
*/
protected function getDataSource($input = null): array
{
return [
'AED', // UAE Dirham
'AFN', // Afghani
'ALL', // Lek
'AMD', // Armenian Dram
'ANG', // Netherlands Antillean Guilder
'AOA', // Kwanza
'ARS', // Argentine Peso
'AUD', // Australian Dollar
'AWG', // Aruban Florin
'AZN', // Azerbaijan Manat
'BAM', // Convertible Mark
'BBD', // Barbados Dollar
'BDT', // Taka
'BGN', // Bulgarian Lev
'BHD', // Bahraini Dinar
'BIF', // Burundi Franc
'BMD', // Bermudian Dollar
'BND', // Brunei Dollar
'BOB', // Boliviano
'BOV', // Mvdol
'BRL', // Brazilian Real
'BSD', // Bahamian Dollar
'BTN', // Ngultrum
'BWP', // Pula
'BYN', // Belarusian Ruble
'BZD', // Belize Dollar
'CAD', // Canadian Dollar
'CDF', // Congolese Franc
'CHE', // WIR Euro
'CHF', // Swiss Franc
'CHW', // WIR Franc
'CLF', // Unidad de Fomento
'CLP', // Chilean Peso
'CNY', // Yuan Renminbi
'COP', // Colombian Peso
'COU', // Unidad de Valor Real
'CRC', // Costa Rican Colon
'CUC', // Peso Convertible
'CUP', // Cuban Peso
'CVE', // Cabo Verde Escudo
'CZK', // Czech Koruna
'DJF', // Djibouti Franc
'DKK', // Danish Krone
'DOP', // Dominican Peso
'DZD', // Algerian Dinar
'EGP', // Egyptian Pound
'ERN', // Nakfa
'ETB', // Ethiopian Birr
'EUR', // Euro
'FJD', // Fiji Dollar
'FKP', // Falkland Islands Pound
'GBP', // Pound Sterling
'GEL', // Lari
'GHS', // Ghana Cedi
'GIP', // Gibraltar Pound
'GMD', // Dalasi
'GNF', // Guinean Franc
'GTQ', // Quetzal
'GYD', // Guyana Dollar
'HKD', // Hong Kong Dollar
'HNL', // Lempira
'HTG', // Gourde
'HUF', // Forint
'IDR', // Rupiah
'ILS', // New Israeli Sheqel
'INR', // Indian Rupee
'IQD', // Iraqi Dinar
'IRR', // Iranian Rial
'ISK', // Iceland Krona
'JMD', // Jamaican Dollar
'JOD', // Jordanian Dinar
'JPY', // Yen
'KES', // Kenyan Shilling
'KGS', // Som
'KHR', // Riel
'KMF', // Comorian Franc
'KPW', // North Korean Won
'KRW', // Won
'KWD', // Kuwaiti Dinar
'KYD', // Cayman Islands Dollar
'KZT', // Tenge
'LAK', // Lao Kip
'LBP', // Lebanese Pound
'LKR', // Sri Lanka Rupee
'LRD', // Liberian Dollar
'LSL', // Loti
'LYD', // Libyan Dinar
'MAD', // Moroccan Dirham
'MDL', // Moldovan Leu
'MGA', // Malagasy Ariary
'MKD', // Denar
'MMK', // Kyat
'MNT', // Tugrik
'MOP', // Pataca
'MRU', // Ouguiya
'MUR', // Mauritius Rupee
'MVR', // Rufiyaa
'MWK', // Malawi Kwacha
'MXN', // Mexican Peso
'MXV', // Mexican Unidad de Inversion (UDI)
'MYR', // Malaysian Ringgit
'MZN', // Mozambique Metical
'NAD', // Namibia Dollar
'NGN', // Naira
'NIO', // Cordoba Oro
'NOK', // Norwegian Krone
'NPR', // Nepalese Rupee
'NZD', // New Zealand Dollar
'OMR', // Rial Omani
'PAB', // Balboa
'PEN', // Sol
'PGK', // Kina
'PHP', // Philippine Peso
'PKR', // Pakistan Rupee
'PLN', // Zloty
'PYG', // Guarani
'QAR', // Qatari Rial
'RON', // Romanian Leu
'RSD', // Serbian Dinar
'RUB', // Russian Ruble
'RWF', // Rwanda Franc
'SAR', // Saudi Riyal
'SBD', // Solomon Islands Dollar
'SCR', // Seychelles Rupee
'SDG', // Sudanese Pound
'SEK', // Swedish Krona
'SGD', // Singapore Dollar
'SHP', // Saint Helena Pound
'SLE', // Leone
'SLL', // Leone
'SOS', // Somali Shilling
'SRD', // Surinam Dollar
'SSP', // South Sudanese Pound
'STN', // Dobra
'SVC', // El Salvador Colon
'SYP', // Syrian Pound
'SZL', // Lilangeni
'THB', // Baht
'TJS', // Somoni
'TMT', // Turkmenistan New Manat
'TND', // Tunisian Dinar
'TOP', // Paanga
'TRY', // Turkish Lira
'TTD', // Trinidad and Tobago Dollar
'TWD', // New Taiwan Dollar
'TZS', // Tanzanian Shilling
'UAH', // Hryvnia
'UGX', // Uganda Shilling
'USD', // US Dollar
'USN', // US Dollar (Next day)
'UYI', // Uruguay Peso en Unidades Indexadas (UI)
'UYU', // Peso Uruguayo
'UYW', // Unidad Previsional
'UZS', // Uzbekistan Sum
'VED', // Bolívar Soberano
'VES', // Bolívar Soberano
'VND', // Dong
'VUV', // Vatu
'WST', // Tala
'XAF', // CFA Franc BEAC
'XAG', // Silver
'XAU', // Gold
'XBA', // Bond Markets Unit European Composite Unit (EURCO)
'XBB', // Bond Markets Unit European Monetary Unit (E.M.U.-6)
'XBC', // Bond Markets Unit European Unit of Account 9 (E.U.A.-9)
'XBD', // Bond Markets Unit European Unit of Account 17 (E.U.A.-17)
'XCD', // East Caribbean Dollar
'XDR', // SDR (Special Drawing Right)
'XOF', // CFA Franc BCEAO
'XPD', // Palladium
'XPF', // CFP Franc
'XPT', // Platinum
'XSU', // Sucre
'XTS', // Codes specifically reserved for testing purposes
'XUA', // ADB Unit of Account
'XXX', // The codes assigned for transactions where no currency is involved
'YER', // Yemeni Rial
'ZAR', // Rand
'ZMW', // Zambian Kwacha
'ZWL', // Zimbabwe Dollar
];
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Helpers\CanValidateDateTime;
use function date;
use function is_scalar;
use function preg_match;
use function sprintf;
use function strtotime;
/**
* Validates if input is a date.
*
* @author Bruno Luiz da Silva <contato@brunoluiz.net>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Date extends AbstractRule
{
use CanValidateDateTime;
/**
* @var string
*/
private $format;
/**
* @var string
*/
private $sample;
/**
* Initializes the rule.
*
* @throws ComponentException
*/
public function __construct(string $format = 'Y-m-d')
{
if (!preg_match('/^[djSFmMnYy\W]+$/', $format)) {
throw new ComponentException(sprintf('"%s" is not a valid date format', $format));
}
$this->format = $format;
$this->sample = date($format, strtotime('2005-12-30'));
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_scalar($input)) {
return false;
}
return $this->isDateTime($this->format, (string) $input);
}
}
+66
View File
@@ -0,0 +1,66 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use DateTimeInterface;
use Respect\Validation\Helpers\CanValidateDateTime;
use function date;
use function is_scalar;
use function strtotime;
/**
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class DateTime extends AbstractRule
{
use CanValidateDateTime;
/**
* @var string|null
*/
private $format;
/**
* @var string
*/
private $sample;
/**
* Initializes the rule.
*/
public function __construct(?string $format = null)
{
$this->format = $format;
$this->sample = date($format ?: 'c', strtotime('2005-12-30 01:02:03'));
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($input instanceof DateTimeInterface) {
return $this->format === null;
}
if (!is_scalar($input)) {
return false;
}
if ($this->format === null) {
return strtotime((string) $input) !== false;
}
return $this->isDateTime($this->format, (string) $input);
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_numeric;
use function is_string;
use function number_format;
use function preg_replace;
use function var_export;
/**
* Validates the decimal
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Decimal extends AbstractRule
{
/**
* @var int
*/
private $decimals;
public function __construct(int $decimals)
{
$this->decimals = $decimals;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_numeric($input)) {
return false;
}
return $this->toFormattedString($input) === $this->toRawString($input);
}
/**
* @param mixed $input
*/
private function toRawString($input): string
{
if (is_string($input)) {
return $input;
}
return var_export($input, true);
}
/**
* @param mixed $input
*/
private function toFormattedString($input): string
{
$formatted = number_format((float) $input, $this->decimals, '.', '');
if (is_string($input)) {
return $formatted;
}
return preg_replace('/^(\d+\.\d)0*$/', '$1', $formatted) ?: '';
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function ctype_digit;
/**
* Validates whether the input contains only digits.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
*/
final class Digit extends AbstractFilterRule
{
/**
* {@inheritDoc}
*/
protected function validateFilteredInput(string $input): bool
{
return ctype_digit($input);
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Directory as NativeDirectory;
use SplFileInfo;
use function is_dir;
use function is_scalar;
/**
* Validates if the given path is a directory.
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Directory extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($input instanceof SplFileInfo) {
return $input->isDir();
}
if ($input instanceof NativeDirectory) {
return true;
}
if (!is_scalar($input)) {
return false;
}
return is_dir((string) $input);
}
}
+180
View File
@@ -0,0 +1,180 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\DomainException;
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validatable;
use function array_merge;
use function array_pop;
use function count;
use function explode;
use function iterator_to_array;
use function mb_substr_count;
/**
* Validates whether the input is a valid domain name or not.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Mehmet Tolga Avcioglu <mehmet@activecom.net>
* @author Nick Lombard <github@jigsoft.co.za>
* @author Róbert Nagy <vrnagy@gmail.com>
*/
final class Domain extends AbstractRule
{
/**
* @var Validatable
*/
private $genericRule;
/**
* @var Validatable
*/
private $tldRule;
/**
* @var Validatable
*/
private $partsRule;
public function __construct(bool $tldCheck = true)
{
$this->genericRule = $this->createGenericRule();
$this->tldRule = $this->createTldRule($tldCheck);
$this->partsRule = $this->createPartsRule();
}
/**
* {@inheritDoc}
*/
public function assert($input): void
{
$exceptions = [];
$this->collectAssertException($exceptions, $this->genericRule, $input);
$this->throwExceptions($exceptions, $input);
$parts = explode('.', (string) $input);
if (count($parts) >= 2) {
$this->collectAssertException($exceptions, $this->tldRule, array_pop($parts));
}
foreach ($parts as $part) {
$this->collectAssertException($exceptions, $this->partsRule, $part);
}
$this->throwExceptions($exceptions, $input);
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
try {
$this->assert($input);
} catch (ValidationException $exception) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
public function check($input): void
{
try {
$this->assert($input);
} catch (NestedValidationException $exception) {
/** @var ValidationException $childException */
foreach ($exception as $childException) {
throw $childException;
}
throw $exception;
}
}
/**
* @param ValidationException[] $exceptions
* @param mixed $input
*/
private function collectAssertException(array &$exceptions, Validatable $validator, $input): void
{
try {
$validator->assert($input);
} catch (NestedValidationException $nestedValidationException) {
$exceptions = array_merge(
$exceptions,
iterator_to_array($nestedValidationException)
);
} catch (ValidationException $validationException) {
$exceptions[] = $validationException;
}
}
private function createGenericRule(): Validatable
{
return new AllOf(
new StringType(),
new NoWhitespace(),
new Contains('.'),
new Length(3)
);
}
private function createTldRule(bool $realTldCheck): Validatable
{
if ($realTldCheck) {
return new Tld();
}
return new AllOf(
new Not(new StartsWith('-')),
new NoWhitespace(),
new Length(2)
);
}
private function createPartsRule(): Validatable
{
return new AllOf(
new Alnum('-'),
new Not(new StartsWith('-')),
new AnyOf(
new Not(new Contains('--')),
new Callback(static function ($str) {
return mb_substr_count($str, '--') == 1;
})
),
new Not(new EndsWith('-'))
);
}
/**
* @param ValidationException[] $exceptions
* @param mixed $input
*/
private function throwExceptions(array $exceptions, $input): void
{
if (count($exceptions)) {
/** @var DomainException $domainException */
$domainException = $this->reportError($input);
$domainException->addChildren($exceptions);
throw $domainException;
}
}
}
+96
View File
@@ -0,0 +1,96 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\EachException;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Helpers\CanValidateIterable;
use Respect\Validation\Validatable;
/**
* Validates whether each value in the input is valid according to another rule.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Each extends AbstractRule
{
use CanValidateIterable;
/**
* @var Validatable
*/
private $rule;
/**
* Initializes the constructor.
*/
public function __construct(Validatable $rule)
{
$this->rule = $rule;
}
/**
* {@inheritDoc}
*/
public function assert($input): void
{
if (!$this->isIterable($input)) {
throw $this->reportError($input);
}
$exceptions = [];
foreach ($input as $value) {
try {
$this->rule->assert($value);
} catch (ValidationException $exception) {
$exceptions[] = $exception;
}
}
if (!empty($exceptions)) {
/** @var EachException $eachException */
$eachException = $this->reportError($input);
$eachException->addChildren($exceptions);
throw $eachException;
}
}
/**
* {@inheritDoc}
*/
public function check($input): void
{
if (!$this->isIterable($input)) {
throw $this->reportError($input);
}
foreach ($input as $value) {
$this->rule->check($value);
}
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
try {
$this->check($input);
} catch (ValidationException $exception) {
return false;
}
return true;
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
use function class_exists;
use function filter_var;
use function is_string;
use const FILTER_VALIDATE_EMAIL;
/**
* Validates an email address.
*
* @author Andrey Kolyshkin <a.kolyshkin@semrush.com>
* @author Eduardo Gulias Davis <me@egulias.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Paul Karikari <paulkarikari1@gmail.com>
*/
final class Email extends AbstractRule
{
/**
* @var EmailValidator|null
*/
private $validator;
/**
* Initializes the rule assigning the EmailValidator instance.
*
* If the EmailValidator instance is not defined, tries to create one.
*/
public function __construct(?EmailValidator $validator = null)
{
$this->validator = $validator ?: $this->createEmailValidator();
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_string($input)) {
return false;
}
if ($this->validator !== null) {
return $this->validator->isValid($input, new RFCValidation());
}
return (bool) filter_var($input, FILTER_VALIDATE_EMAIL);
}
private function createEmailValidator(): ?EmailValidator
{
if (class_exists(EmailValidator::class)) {
return new EmailValidator();
}
return null;
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function end;
use function is_array;
use function mb_strlen;
use function mb_strripos;
use function mb_strrpos;
/**
* Validates only if the value is at the end of the input.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Hugo Hamon <hugo.hamon@sensiolabs.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class EndsWith extends AbstractRule
{
/**
* @var mixed
*/
private $endValue;
/**
* @var bool
*/
private $identical;
/**
* @param mixed $endValue
*/
public function __construct($endValue, bool $identical = false)
{
$this->endValue = $endValue;
$this->identical = $identical;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($this->identical) {
return $this->validateIdentical($input);
}
return $this->validateEquals($input);
}
/**
* @param mixed $input
*/
private function validateEquals($input): bool
{
if (is_array($input)) {
return end($input) == $this->endValue;
}
return mb_strripos($input, $this->endValue) === mb_strlen($input) - mb_strlen($this->endValue);
}
/**
* @param mixed $input
*/
private function validateIdentical($input): bool
{
if (is_array($input)) {
return end($input) === $this->endValue;
}
return mb_strrpos($input, $this->endValue) === mb_strlen($input) - mb_strlen($this->endValue);
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates if the input is equal to some value.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Hugo Hamon <hugo.hamon@sensiolabs.com>
*/
final class Equals extends AbstractRule
{
/**
* @var mixed
*/
private $compareTo;
/**
* Initializes the rule.
*
* @param mixed $compareTo
*/
public function __construct($compareTo)
{
$this->compareTo = $compareTo;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return $input == $this->compareTo;
}
}
@@ -0,0 +1,57 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_scalar;
use function mb_strtoupper;
/**
* Validates if the input is equivalent to some value.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Equivalent extends AbstractRule
{
/**
* @var mixed
*/
private $compareTo;
/**
* Initializes the rule.
*
* @param mixed $compareTo
*/
public function __construct($compareTo)
{
$this->compareTo = $compareTo;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (is_scalar($input)) {
return $this->isStringEquivalent((string) $input);
}
return $input == $this->compareTo;
}
private function isStringEquivalent(string $input): bool
{
if (!is_scalar($this->compareTo)) {
return false;
}
return mb_strtoupper((string) $input) === mb_strtoupper((string) $this->compareTo);
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function filter_var;
use const FILTER_VALIDATE_INT;
/**
* Validates whether the input is an even number or not.
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jean Pimentel <jeanfap@gmail.com>
* @author Paul Karikari <paulkarikari1@gmail.com>
*/
final class Even extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (filter_var($input, FILTER_VALIDATE_INT) === false) {
return false;
}
return (int) $input % 2 === 0;
}
}
@@ -0,0 +1,40 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use SplFileInfo;
use function is_executable;
use function is_scalar;
/**
* Validates if a file is an executable.
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Executable extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($input instanceof SplFileInfo) {
return $input->isExecutable();
}
if (!is_scalar($input)) {
return false;
}
return is_executable((string) $input);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use SplFileInfo;
use function file_exists;
use function is_string;
/**
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class Exists extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($input instanceof SplFileInfo) {
$input = $input->getPathname();
}
return is_string($input) && file_exists($input);
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use SplFileInfo;
use function is_string;
use function pathinfo;
use const PATHINFO_EXTENSION;
/**
* Validate file extensions.
*
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Extension extends AbstractRule
{
/**
* @var string
*/
private $extension;
/**
* Initializes the rule.
*/
public function __construct(string $extension)
{
$this->extension = $extension;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($input instanceof SplFileInfo) {
return $this->extension === $input->getExtension();
}
if (!is_string($input)) {
return false;
}
return $this->extension === pathinfo($input, PATHINFO_EXTENSION);
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function abs;
use function is_integer;
use function is_numeric;
/**
* Validates if the input is a factor of the defined dividend.
*
* @author Danilo Correa <danilosilva87@gmail.com>
* @author David Meister <thedavidmeister@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Factor extends AbstractRule
{
/**
* @var int
*/
private $dividend;
/**
* Initializes the rule.
*/
public function __construct(int $dividend)
{
$this->dividend = $dividend;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
// Every integer is a factor of zero, and zero is the only integer that
// has zero for a factor.
if ($this->dividend === 0) {
return true;
}
// Factors must be integers that are not zero.
if (!is_numeric($input) || (int) $input != $input || $input == 0) {
return false;
}
$input = (int) abs((int) $input);
$dividend = (int) abs($this->dividend);
// The dividend divided by the input must be an integer if input is a
// factor of the dividend.
return is_integer($dividend / $input);
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function filter_var;
use const FILTER_NULL_ON_FAILURE;
use const FILTER_VALIDATE_BOOLEAN;
/**
* Validates if a value is considered as false.
*
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class FalseVal extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === false;
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_numeric;
/**
* Validates whether the input follows the Fibonacci integer sequence.
*
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Samuel Heinzmann <samuel.heinzmann@swisscom.com>
*/
final class Fibonacci extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_numeric($input)) {
return false;
}
$sequence = [0, 1];
$position = 1;
while ($input > $sequence[$position]) {
++$position;
$sequence[$position] = $sequence[$position - 1] + $sequence[$position - 2];
}
return $sequence[$position] === (int) $input;
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use SplFileInfo;
use function is_file;
use function is_string;
/**
* Validates whether file input is as a regular filename.
*
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class File extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($input instanceof SplFileInfo) {
return $input->isFile();
}
return is_string($input) && is_file($input);
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use function array_key_exists;
use function filter_var;
use function is_array;
use function is_int;
use const FILTER_VALIDATE_BOOLEAN;
use const FILTER_VALIDATE_DOMAIN;
use const FILTER_VALIDATE_EMAIL;
use const FILTER_VALIDATE_FLOAT;
use const FILTER_VALIDATE_INT;
use const FILTER_VALIDATE_IP;
use const FILTER_VALIDATE_REGEXP;
use const FILTER_VALIDATE_URL;
/**
* Validates the input with the PHP's filter_var() function.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class FilterVar extends AbstractEnvelope
{
private const ALLOWED_FILTERS = [
FILTER_VALIDATE_BOOLEAN => 'is_bool',
FILTER_VALIDATE_DOMAIN => 'is_string',
FILTER_VALIDATE_EMAIL => 'is_string',
FILTER_VALIDATE_FLOAT => 'is_float',
FILTER_VALIDATE_INT => 'is_int',
FILTER_VALIDATE_IP => 'is_string',
FILTER_VALIDATE_REGEXP => 'is_string',
FILTER_VALIDATE_URL => 'is_string',
];
/**
* Initializes the rule.
*
* @param mixed $options
*
* @throws ComponentException
*/
public function __construct(int $filter, $options = [])
{
if (!array_key_exists($filter, self::ALLOWED_FILTERS)) {
throw new ComponentException('Cannot accept the given filter');
}
$arguments = [$filter];
if (is_array($options) || is_int($options)) {
$arguments[] = $options;
}
parent::__construct(new Callback(static function ($input) use ($filter, $arguments) {
return (self::ALLOWED_FILTERS[$filter])(
filter_var($input, ...$arguments)
);
}));
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_finite;
use function is_numeric;
/**
* Validates if the input is a finite number.
*
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Finite extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_numeric($input) && is_finite((float) $input);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_float;
/**
* Validates whether the type of the input is float.
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Reginaldo Junior <76regi@gmail.com>
*/
final class FloatType extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_float($input);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function filter_var;
use function is_float;
use const FILTER_VALIDATE_FLOAT;
/**
* Validate whether the input value is float.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jayson Reis <santosdosreis@gmail.com>
*/
final class FloatVal extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_float(filter_var($input, FILTER_VALIDATE_FLOAT));
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function ctype_graph;
/**
* Validates if all characters in the input are printable and actually creates visible output (no white space).
*
* @author Andre Ramaciotti <andre@ramaciotti.com>
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Nick Lombard <github@jigsoft.co.za>
*/
final class Graph extends AbstractFilterRule
{
/**
* {@inheritDoc}
*/
protected function validateFilteredInput(string $input): bool
{
return ctype_graph($input);
}
}
@@ -0,0 +1,26 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates whether the input is less than a value.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class GreaterThan extends AbstractComparison
{
/**
* {@inheritDoc}
*/
protected function compare($left, $right): bool
{
return $left > $right;
}
}
@@ -0,0 +1,24 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates weather the input is a hex RGB color or not.
*
* @author Davide Pastore <pasdavide@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class HexRgbColor extends AbstractEnvelope
{
public function __construct()
{
parent::__construct(new Regex('/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i'));
}
}
+149
View File
@@ -0,0 +1,149 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function bcmod;
use function is_string;
use function ord;
use function preg_match;
use function preg_replace_callback;
use function str_replace;
use function strlen;
use function strval;
use function substr;
/**
* Validates whether the input is a valid IBAN (International Bank Account Number) or not.
*
* @author Mazen Touati <mazen_touati@hotmail.com>
*/
final class Iban extends AbstractRule
{
private const COUNTRIES_LENGTHS = [
'AL' => 28,
'AD' => 24,
'AT' => 20,
'AZ' => 28,
'BH' => 22,
'BE' => 16,
'BA' => 20,
'BR' => 29,
'BG' => 22,
'CR' => 21,
'HR' => 21,
'CY' => 28,
'CZ' => 24,
'DK' => 18,
'DO' => 28,
'EE' => 20,
'FO' => 18,
'FI' => 18,
'FR' => 27,
'GE' => 22,
'DE' => 22,
'GI' => 23,
'GR' => 27,
'GL' => 18,
'GT' => 28,
'HU' => 28,
'IS' => 26,
'IE' => 22,
'IL' => 23,
'IT' => 27,
'JO' => 30,
'KZ' => 20,
'KW' => 30,
'LV' => 21,
'LB' => 28,
'LI' => 21,
'LT' => 20,
'LU' => 20,
'MK' => 19,
'MT' => 31,
'MR' => 27,
'MU' => 30,
'MD' => 24,
'MC' => 27,
'ME' => 22,
'NL' => 18,
'NO' => 15,
'PK' => 24,
'PL' => 28,
'PS' => 29,
'PT' => 25,
'QA' => 29,
'XK' => 20,
'RO' => 24,
'LC' => 32,
'SM' => 27,
'ST' => 25,
'SA' => 24,
'RS' => 22,
'SC' => 31,
'SK' => 24,
'SI' => 19,
'ES' => 24,
'SE' => 24,
'CH' => 21,
'TL' => 23,
'TN' => 24,
'TR' => 26,
'UA' => 29,
'AE' => 23,
'GB' => 22,
'VG' => 24,
];
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_string($input)) {
return false;
}
$iban = str_replace(' ', '', $input);
if (!preg_match('/[A-Z0-9]{15,34}/', $iban)) {
return false;
}
$countryCode = substr($iban, 0, 2);
if (!$this->hasValidCountryLength($iban, $countryCode)) {
return false;
}
$checkDigits = substr($iban, 2, 2);
$bban = substr($iban, 4);
$rearranged = $bban . $countryCode . $checkDigits;
return bcmod($this->convertToIntegerAsString($rearranged), '97') === '1';
}
private function hasValidCountryLength(string $iban, string $countryCode): bool
{
if (!isset(self::COUNTRIES_LENGTHS[$countryCode])) {
return false;
}
return strlen($iban) === self::COUNTRIES_LENGTHS[$countryCode];
}
private function convertToIntegerAsString(string $reArrangedIban): string
{
return (string) preg_replace_callback(
'/[A-Z]/',
static function (array $match): string {
return strval(ord($match[0]) - 55);
},
$reArrangedIban
);
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates if the input is identical to some value.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Identical extends AbstractRule
{
/**
* @var mixed
*/
private $compareTo;
/**
* Initializes the rule.
*
* @param mixed $compareTo
*/
public function __construct($compareTo)
{
$this->compareTo = $compareTo;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return $input === $this->compareTo;
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use finfo;
use SplFileInfo;
use function is_file;
use function is_string;
use function mb_strpos;
use const FILEINFO_MIME_TYPE;
/**
* Validates if the file is a valid image by checking its MIME type.
*
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Guilherme Siani <guilherme@siani.com.br>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Image extends AbstractRule
{
/**
* @var finfo
*/
private $fileInfo;
/**
* Initializes the rule.
*/
public function __construct(?finfo $fileInfo = null)
{
$this->fileInfo = $fileInfo ?: new finfo(FILEINFO_MIME_TYPE);
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($input instanceof SplFileInfo) {
return $this->validate($input->getPathname());
}
if (!is_string($input)) {
return false;
}
if (!is_file($input)) {
return false;
}
return mb_strpos((string) $this->fileInfo->file($input), 'image/') === 0;
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_scalar;
use function mb_strlen;
use function preg_replace;
/**
* Validates is the input is a valid IMEI.
*
* @author Alexander Gorshkov <mazanax@yandex.ru>
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Diego Oliveira <contato@diegoholiveira.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Imei extends AbstractRule
{
private const IMEI_SIZE = 15;
/**
* @see https://en.wikipedia.org/wiki/International_Mobile_Station_Equipment_Identity
*
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_scalar($input)) {
return false;
}
$numbers = (string) preg_replace('/\D/', '', (string) $input);
if (mb_strlen($numbers) != self::IMEI_SIZE) {
return false;
}
return (new Luhn())->validate($numbers);
}
}
+90
View File
@@ -0,0 +1,90 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function in_array;
use function is_array;
use function mb_stripos;
use function mb_strpos;
/**
* Validates if the input can be found in a defined array or string.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class In extends AbstractRule
{
/**
* @var mixed[]|mixed
*/
private $haystack;
/**
* @var bool
*/
private $compareIdentical;
/**
* Initializes the rule with the haystack and optionally compareIdentical flag.
*
* @param mixed[]|mixed $haystack
*/
public function __construct($haystack, bool $compareIdentical = false)
{
$this->haystack = $haystack;
$this->compareIdentical = $compareIdentical;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($this->compareIdentical) {
return $this->validateIdentical($input);
}
return $this->validateEquals($input);
}
/**
* @param mixed $input
*/
private function validateEquals($input): bool
{
if (is_array($this->haystack)) {
return in_array($input, $this->haystack);
}
if ($input === null || $input === '') {
return $input == $this->haystack;
}
return mb_stripos($this->haystack, (string) $input) !== false;
}
/**
* @param mixed $input
*/
private function validateIdentical($input): bool
{
if (is_array($this->haystack)) {
return in_array($input, $this->haystack, true);
}
if ($input === null || $input === '') {
return $input === $this->haystack;
}
return mb_strpos($this->haystack, (string) $input) !== false;
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_infinite;
use function is_numeric;
/**
* Validates if the input is an infinite number
*
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Infinite extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_numeric($input) && is_infinite((float) $input);
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates if the input is an instance of the given class or interface.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Instance extends AbstractRule
{
/**
* @var string
*/
private $instanceName;
/**
* Initializes the rule with the expected instance name.
*/
public function __construct(string $instanceName)
{
$this->instanceName = $instanceName;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return $input instanceof $this->instanceName;
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_int;
/**
* Validates whether the type of the input is integer.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class IntType extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return is_int($input);
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_int;
use function is_string;
use function preg_match;
/**
* Validates if the input is an integer.
*
* @author Adam Benson <adam.benson@bigcommerce.com>
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Andrei Drulchenko <andrdru@gmail.com>
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class IntVal extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (is_int($input)) {
return true;
}
if (!is_string($input)) {
return false;
}
return preg_match('/^-?\d+$/', $input) === 1;
}
}
+209
View File
@@ -0,0 +1,209 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use function bccomp;
use function explode;
use function filter_var;
use function ip2long;
use function is_string;
use function long2ip;
use function mb_strpos;
use function mb_substr_count;
use function sprintf;
use function str_repeat;
use function str_replace;
use function strtr;
use const FILTER_VALIDATE_IP;
/**
* Validates whether the input is a valid IP address.
*
* This validator uses the native filter_var() PHP function.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
*/
final class Ip extends AbstractRule
{
/**
* @var string|null
*/
private $range;
/**
* @var int|null
*/
private $options;
/**
* @var string|null
*/
private $startAddress;
/**
* @var string|null
*/
private $endAddress;
/**
* @var string|null
*/
private $mask;
/**
* Initializes the rule defining the range and some options for filter_var().
*
* @throws ComponentException In case the range is invalid
*/
public function __construct(string $range = '*', ?int $options = null)
{
$this->parseRange($range);
$this->range = $this->createRange();
$this->options = $options;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_string($input)) {
return false;
}
if (!$this->verifyAddress($input)) {
return false;
}
if ($this->mask) {
return $this->belongsToSubnet($input);
}
if ($this->startAddress && $this->endAddress) {
return $this->verifyNetwork($input);
}
return true;
}
private function createRange(): ?string
{
if ($this->startAddress && $this->endAddress) {
return $this->startAddress . '-' . $this->endAddress;
}
if ($this->startAddress && $this->mask) {
return $this->startAddress . '/' . long2ip((int) $this->mask);
}
return null;
}
private function parseRange(string $input): void
{
if ($input == '*' || $input == '*.*.*.*' || $input == '0.0.0.0-255.255.255.255') {
return;
}
if (mb_strpos($input, '-') !== false) {
[$this->startAddress, $this->endAddress] = explode('-', $input);
if ($this->startAddress !== null && !$this->verifyAddress($this->startAddress)) {
throw new ComponentException('Invalid network range');
}
if ($this->endAddress !== null && !$this->verifyAddress($this->endAddress)) {
throw new ComponentException('Invalid network range');
}
return;
}
if (mb_strpos($input, '*') !== false) {
$this->parseRangeUsingWildcards($input);
return;
}
if (mb_strpos($input, '/') !== false) {
$this->parseRangeUsingCidr($input);
return;
}
throw new ComponentException('Invalid network range');
}
private function fillAddress(string $address, string $fill = '*'): string
{
return $address . str_repeat('.' . $fill, 3 - mb_substr_count($address, '.'));
}
private function parseRangeUsingWildcards(string $input): void
{
$address = $this->fillAddress($input);
$this->startAddress = strtr($address, '*', '0');
$this->endAddress = str_replace('*', '255', $address);
}
private function parseRangeUsingCidr(string $input): void
{
$parts = explode('/', $input);
$this->startAddress = $this->fillAddress($parts[0], '0');
$isAddressMask = mb_strpos($parts[1], '.') !== false;
if ($isAddressMask && $this->verifyAddress($parts[1])) {
$this->mask = sprintf('%032b', ip2long($parts[1]));
return;
}
if ($isAddressMask || $parts[1] < 8 || $parts[1] > 30) {
throw new ComponentException('Invalid network mask');
}
$this->mask = sprintf('%032b', ip2long((string) long2ip(~(2 ** (32 - (int) $parts[1]) - 1))));
}
private function verifyAddress(string $address): bool
{
return filter_var($address, FILTER_VALIDATE_IP, ['flags' => $this->options]) !== false;
}
private function verifyNetwork(string $input): bool
{
$input = sprintf('%u', ip2long($input));
return $this->startAddress !== null
&& $this->endAddress !== null
&& bccomp($input, sprintf('%u', ip2long($this->startAddress))) >= 0
&& bccomp($input, sprintf('%u', ip2long($this->endAddress))) <= 0;
}
private function belongsToSubnet(string $input): bool
{
if ($this->mask === null || $this->startAddress === null) {
return false;
}
$min = sprintf('%032b', ip2long($this->startAddress));
$input = sprintf('%032b', ip2long($input));
return ($input & $this->mask) === ($min & $this->mask);
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function implode;
use function is_scalar;
use function preg_match;
use function sprintf;
/**
* Validates whether the input is a valid ISBN (International Standard Book Number) or not.
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Moritz Fromm <moritzgitfromm@gmail.com>
*/
final class Isbn extends AbstractRule
{
/**
* @see https://howtodoinjava.com/regex/java-regex-validate-international-standard-book-number-isbns
*/
private const PIECES = [
'^(?:ISBN(?:-1[03])?:? )?(?=[0-9X]{10}$|(?=(?:[0-9]+[- ]){3})',
'[- 0-9X]{13}$|97[89][0-9]{10}$|(?=(?:[0-9]+[- ]){4})[- 0-9]{17}$)',
'(?:97[89][- ]?)?[0-9]{1,5}[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9X]$',
];
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_scalar($input)) {
return false;
}
return preg_match(sprintf('/%s/', implode(self::PIECES)), (string) $input) > 0;
}
}
@@ -0,0 +1,30 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Helpers\CanValidateIterable;
/**
* Validates whether the pseudo-type of the input is iterable or not.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class IterableType extends AbstractRule
{
use CanValidateIterable;
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
return $this->isIterable($input);
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function function_exists;
use function is_string;
use function json_decode;
use function json_last_error;
use const JSON_ERROR_NONE;
/**
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Json extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_string($input) || $input === '') {
return false;
}
if (function_exists('json_validate')) {
return json_validate($input);
}
json_decode($input);
return json_last_error() === JSON_ERROR_NONE;
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Validatable;
use function array_key_exists;
use function is_array;
use function is_scalar;
/**
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Key extends AbstractRelated
{
/**
* @param mixed $reference
*/
public function __construct($reference, ?Validatable $rule = null, bool $mandatory = true)
{
if (!is_scalar($reference) || $reference === '') {
throw new ComponentException('Invalid array key name');
}
parent::__construct($reference, $rule, $mandatory);
}
/**
* {@inheritDoc}
*/
public function getReferenceValue($input)
{
return $input[$this->getReference()];
}
/**
* {@inheritDoc}
*/
public function hasReference($input): bool
{
return is_array($input) && array_key_exists($this->getReference(), $input);
}
}
+146
View File
@@ -0,0 +1,146 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use ArrayAccess;
use Respect\Validation\Exceptions\ComponentException;
use function array_key_exists;
use function array_shift;
use function explode;
use function is_array;
use function is_null;
use function is_object;
use function is_scalar;
use function property_exists;
use function rtrim;
use function sprintf;
/**
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Ivan Zinovyev <vanyazin@gmail.com>
*/
final class KeyNested extends AbstractRelated
{
/**
* {@inheritDoc}
*/
public function hasReference($input): bool
{
try {
$this->getReferenceValue($input);
} catch (ComponentException $cex) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
public function getReferenceValue($input)
{
if (is_scalar($input)) {
$message = sprintf('Cannot select the %s in the given data', $this->getReference());
throw new ComponentException($message);
}
$keys = $this->getReferencePieces();
$value = $input;
while (!is_null($key = array_shift($keys))) {
$value = $this->getValue($value, $key);
}
return $value;
}
/**
* @return string[]
*/
private function getReferencePieces(): array
{
return explode('.', rtrim((string) $this->getReference(), '.'));
}
/**
* @param mixed[] $array
* @param mixed $key
*
* @return mixed
*/
private function getValueFromArray(array $array, $key)
{
if (!array_key_exists($key, $array)) {
$message = sprintf('Cannot select the key %s from the given array', $this->getReference());
throw new ComponentException($message);
}
return $array[$key];
}
/**
* @param ArrayAccess<mixed, mixed> $array
* @param mixed $key
*
* @return mixed
*/
private function getValueFromArrayAccess(ArrayAccess $array, $key)
{
if (!$array->offsetExists($key)) {
$message = sprintf('Cannot select the key %s from the given array', $this->getReference());
throw new ComponentException($message);
}
return $array->offsetGet($key);
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
*
*
* @return mixed
*/
private function getValueFromObject(object $object, string $property)
{
if (empty($property) || !property_exists($object, $property)) {
$message = sprintf('Cannot select the property %s from the given object', $this->getReference());
throw new ComponentException($message);
}
return $object->{$property};
}
/**
* @param mixed $value
* @param mixed $key
*
* @return mixed
*/
private function getValue($value, $key)
{
if (is_array($value)) {
return $this->getValueFromArray($value, $key);
}
if ($value instanceof ArrayAccess) {
return $this->getValueFromArrayAccess($value, $key);
}
if (is_object($value)) {
return $this->getValueFromObject($value, $key);
}
$message = sprintf('Cannot select the property %s from the given data', $this->getReference());
throw new ComponentException($message);
}
}
+142
View File
@@ -0,0 +1,142 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\NonNegatable;
use Respect\Validation\Validatable;
use function array_key_exists;
use function array_map;
use function count;
use function current;
use function is_array;
/**
* Validates a keys in a defined structure.
*
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class KeySet extends AbstractWrapper implements NonNegatable
{
/**
* @var mixed[]
*/
private $keys;
/**
* @var mixed[]
*/
private $extraKeys = [];
/**
* @var Key[]
*/
private $keyRules;
/**
* Initializes the rule.
*
* phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.UselessAnnotation
* @param Validatable ...$validatables
*/
public function __construct(Validatable ...$validatables)
{
$this->keyRules = array_map([$this, 'getKeyRule'], $validatables);
$this->keys = array_map([$this, 'getKeyReference'], $this->keyRules);
parent::__construct(new AllOf(...$this->keyRules));
}
/**
* {@inheritDoc}
*/
public function assert($input): void
{
if (!$this->hasValidStructure($input)) {
throw $this->reportError($input);
}
parent::assert($input);
}
/**
* {@inheritDoc}
*/
public function check($input): void
{
if (!$this->hasValidStructure($input)) {
throw $this->reportError($input);
}
parent::check($input);
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!$this->hasValidStructure($input)) {
return false;
}
return parent::validate($input);
}
/**
* @throws ComponentException
*/
private function getKeyRule(Validatable $validatable): Key
{
if ($validatable instanceof Key) {
return $validatable;
}
if (!$validatable instanceof AllOf || count($validatable->getRules()) !== 1) {
throw new ComponentException('KeySet rule accepts only Key rules');
}
return $this->getKeyRule(current($validatable->getRules()));
}
/**
* @return mixed
*/
private function getKeyReference(Key $rule)
{
return $rule->getReference();
}
/**
* @param mixed $input
*/
private function hasValidStructure($input): bool
{
if (!is_array($input)) {
return false;
}
foreach ($this->keyRules as $keyRule) {
if (!array_key_exists($keyRule->getReference(), $input) && $keyRule->isMandatory()) {
return false;
}
unset($input[$keyRule->getReference()]);
}
foreach ($input as $extraKey => &$ignoreValue) {
$this->extraKeys[] = $extraKey;
}
return count($input) == 0;
}
}
+144
View File
@@ -0,0 +1,144 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Factory;
use Respect\Validation\Validatable;
use function array_keys;
use function in_array;
/**
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class KeyValue extends AbstractRule
{
/**
* @var int|string
*/
private $comparedKey;
/**
* @var string
*/
private $ruleName;
/**
* @var int|string
*/
private $baseKey;
/**
* @param int|string $comparedKey
* @param int|string $baseKey
*/
public function __construct($comparedKey, string $ruleName, $baseKey)
{
$this->comparedKey = $comparedKey;
$this->ruleName = $ruleName;
$this->baseKey = $baseKey;
}
/**
* {@inheritDoc}
*/
public function assert($input): void
{
$rule = $this->getRule($input);
try {
$rule->assert($input[$this->comparedKey]);
} catch (ValidationException $exception) {
throw $this->overwriteExceptionParams($exception);
}
}
/**
* {@inheritDoc}
*/
public function check($input): void
{
$rule = $this->getRule($input);
try {
$rule->check($input[$this->comparedKey]);
} catch (ValidationException $exception) {
throw $this->overwriteExceptionParams($exception);
}
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
try {
$rule = $this->getRule($input);
} catch (ValidationException $e) {
return false;
}
return $rule->validate($input[$this->comparedKey]);
}
/**
* {@inheritDoc}
*/
public function reportError($input, array $extraParams = []): ValidationException
{
try {
return $this->overwriteExceptionParams($this->getRule($input)->reportError($input));
} catch (ValidationException $exception) {
return $this->overwriteExceptionParams($exception);
}
}
/**
* @param mixed $input
*/
private function getRule($input): Validatable
{
if (!isset($input[$this->comparedKey])) {
throw parent::reportError($this->comparedKey);
}
if (!isset($input[$this->baseKey])) {
throw parent::reportError($this->baseKey);
}
try {
$rule = Factory::getDefaultInstance()->rule($this->ruleName, [$input[$this->baseKey]]);
$rule->setName((string) $this->comparedKey);
} catch (ComponentException $exception) {
throw parent::reportError($input, ['component' => true]);
}
return $rule;
}
private function overwriteExceptionParams(ValidationException $exception): ValidationException
{
$params = [];
foreach (array_keys($exception->getParams()) as $key) {
if (in_array($key, ['template', 'translator'])) {
continue;
}
$params[$key] = $this->baseKey;
}
$params['name'] = $this->comparedKey;
$exception->updateParams($params);
return $exception;
}
}
@@ -0,0 +1,549 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use function array_column;
use function array_filter;
use function array_search;
use function sprintf;
/**
* Validates whether the input is language code based on ISO 639.
*
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class LanguageCode extends AbstractEnvelope
{
public const ALPHA2 = 'alpha-2';
public const ALPHA3 = 'alpha-3';
public const AVAILABLE_SETS = [self::ALPHA2, self::ALPHA3];
/**
* @see http://www.loc.gov/standards/iso639-2/ISO-639-2_utf-8.txt
*/
public const LANGUAGE_CODES = [
// phpcs:disable Squiz.PHP.CommentedOutCode.Found
['aa', 'aar'], // Afar
['ab', 'abk'], // Abkhazian
['', 'ace'], // Achinese
['', 'ach'], // Acoli
['', 'ada'], // Adangme
['', 'ady'], // Adyghe; Adygei
['', 'afa'], // Afro-Asiatic languages
['', 'afh'], // Afrihili
['af', 'afr'], // Afrikaans
['', 'ain'], // Ainu
['ak', 'aka'], // Akan
['', 'akk'], // Akkadian
['sq', 'alb'], // Albanian
['', 'ale'], // Aleut
['', 'alg'], // Algonquian languages
['', 'alt'], // Southern Altai
['am', 'amh'], // Amharic
['', 'ang'], // English, Old (ca.450-1100)
['', 'anp'], // Angika
['', 'apa'], // Apache languages
['ar', 'ara'], // Arabic
['', 'arc'], // Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)
['an', 'arg'], // Aragonese
['hy', 'arm'], // Armenian
['', 'arn'], // Mapudungun; Mapuche
['', 'arp'], // Arapaho
['', 'art'], // Artificial languages
['', 'arw'], // Arawak
['as', 'asm'], // Assamese
['', 'ast'], // Asturian; Bable; Leonese; Asturleonese
['', 'ath'], // Athapascan languages
['', 'aus'], // Australian languages
['av', 'ava'], // Avaric
['ae', 'ave'], // Avestan
['', 'awa'], // Awadhi
['ay', 'aym'], // Aymara
['az', 'aze'], // Azerbaijani
['', 'bad'], // Banda languages
['', 'bai'], // Bamileke languages
['ba', 'bak'], // Bashkir
['', 'bal'], // Baluchi
['bm', 'bam'], // Bambara
['', 'ban'], // Balinese
['eu', 'baq'], // Basque
['', 'bas'], // Basa
['', 'bat'], // Baltic languages
['', 'bej'], // Beja; Bedawiyet
['be', 'bel'], // Belarusian
['', 'bem'], // Bemba
['bn', 'ben'], // Bengali
['', 'ber'], // Berber languages
['', 'bho'], // Bhojpuri
['bh', 'bih'], // Bihari languages
['', 'bik'], // Bikol
['', 'bin'], // Bini; Edo
['bi', 'bis'], // Bislama
['', 'bla'], // Siksika
['', 'bnt'], // Bantu languages
['bs', 'bos'], // Bosnian
['', 'bra'], // Braj
['br', 'bre'], // Breton
['', 'btk'], // Batak languages
['', 'bua'], // Buriat
['', 'bug'], // Buginese
['bg', 'bul'], // Bulgarian
['my', 'bur'], // Burmese
['', 'byn'], // Blin; Bilin
['', 'cad'], // Caddo
['', 'cai'], // Central American Indian languages
['', 'car'], // Galibi Carib
['ca', 'cat'], // Catalan; Valencian
['', 'cau'], // Caucasian languages
['', 'ceb'], // Cebuano
['', 'cel'], // Celtic languages
['ch', 'cha'], // Chamorro
['', 'chb'], // Chibcha
['ce', 'che'], // Chechen
['', 'chg'], // Chagatai
['zh', 'chi'], // Chinese
['', 'chk'], // Chuukese
['', 'chm'], // Mari
['', 'chn'], // Chinook jargon
['', 'cho'], // Choctaw
['', 'chp'], // Chipewyan; Dene Suline
['', 'chr'], // Cherokee
['cu', 'chu'], // Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic
['cv', 'chv'], // Chuvash
['', 'chy'], // Cheyenne
['', 'cmc'], // Chamic languages
['', 'cnr'], // Montenegrin
['', 'cop'], // Coptic
['kw', 'cor'], // Cornish
['co', 'cos'], // Corsican
['', 'cpe'], // Creoles and pidgins, English based
['', 'cpf'], // Creoles and pidgins, French-based
['', 'cpp'], // Creoles and pidgins, Portuguese-based
['cr', 'cre'], // Cree
['', 'crh'], // Crimean Tatar; Crimean Turkish
['', 'crp'], // Creoles and pidgins
['', 'csb'], // Kashubian
['', 'cus'], // Cushitic languages
['cs', 'cze'], // Czech
['', 'dak'], // Dakota
['da', 'dan'], // Danish
['', 'dar'], // Dargwa
['', 'day'], // Land Dayak languages
['', 'del'], // Delaware
['', 'den'], // Slave (Athapascan)
['', 'dgr'], // Dogrib
['', 'din'], // Dinka
['dv', 'div'], // Divehi; Dhivehi; Maldivian
['', 'doi'], // Dogri
['', 'dra'], // Dravidian languages
['', 'dsb'], // Lower Sorbian
['', 'dua'], // Duala
['', 'dum'], // Dutch, Middle (ca.1050-1350)
['nl', 'dut'], // Dutch; Flemish
['', 'dyu'], // Dyula
['dz', 'dzo'], // Dzongkha
['', 'efi'], // Efik
['', 'egy'], // Egyptian (Ancient)
['', 'eka'], // Ekajuk
['', 'elx'], // Elamite
['en', 'eng'], // English
['', 'enm'], // English, Middle (1100-1500)
['eo', 'epo'], // Esperanto
['et', 'est'], // Estonian
['ee', 'ewe'], // Ewe
['', 'ewo'], // Ewondo
['', 'fan'], // Fang
['fo', 'fao'], // Faroese
['', 'fat'], // Fanti
['fj', 'fij'], // Fijian
['', 'fil'], // Filipino; Pilipino
['fi', 'fin'], // Finnish
['', 'fiu'], // Finno-Ugrian languages
['', 'fon'], // Fon
['fr', 'fre'], // French
['', 'frm'], // French, Middle (ca.1400-1600)
['', 'fro'], // French, Old (842-ca.1400)
['', 'frr'], // Northern Frisian
['', 'frs'], // Eastern Frisian
['fy', 'fry'], // Western Frisian
['ff', 'ful'], // Fulah
['', 'fur'], // Friulian
['', 'gaa'], // Ga
['', 'gay'], // Gayo
['', 'gba'], // Gbaya
['', 'gem'], // Germanic languages
['ka', 'geo'], // Georgian
['de', 'ger'], // German
['', 'gez'], // Geez
['', 'gil'], // Gilbertese
['gd', 'gla'], // Gaelic; Scottish Gaelic
['ga', 'gle'], // Irish
['gl', 'glg'], // Galician
['gv', 'glv'], // Manx
['', 'gmh'], // German, Middle High (ca.1050-1500)
['', 'goh'], // German, Old High (ca.750-1050)
['', 'gon'], // Gondi
['', 'gor'], // Gorontalo
['', 'got'], // Gothic
['', 'grb'], // Grebo
['', 'grc'], // Greek, Ancient (to 1453)
['el', 'gre'], // Greek, Modern (1453-)
['gn', 'grn'], // Guarani
['', 'gsw'], // Swiss German; Alemannic; Alsatian
['gu', 'guj'], // Gujarati
['', 'gwi'], // Gwich'in
['', 'hai'], // Haida
['ht', 'hat'], // Haitian; Haitian Creole
['ha', 'hau'], // Hausa
['', 'haw'], // Hawaiian
['he', 'heb'], // Hebrew
['hz', 'her'], // Herero
['', 'hil'], // Hiligaynon
['', 'him'], // Himachali languages; Western Pahari languages
['hi', 'hin'], // Hindi
['', 'hit'], // Hittite
['', 'hmn'], // Hmong; Mong
['ho', 'hmo'], // Hiri Motu
['hr', 'hrv'], // Croatian
['', 'hsb'], // Upper Sorbian
['hu', 'hun'], // Hungarian
['', 'hup'], // Hupa
['', 'iba'], // Iban
['ig', 'ibo'], // Igbo
['is', 'ice'], // Icelandic
['io', 'ido'], // Ido
['ii', 'iii'], // Sichuan Yi; Nuosu
['', 'ijo'], // Ijo languages
['iu', 'iku'], // Inuktitut
['ie', 'ile'], // Interlingue; Occidental
['', 'ilo'], // Iloko
['ia', 'ina'], // Interlingua (International Auxiliary Language Association)
['', 'inc'], // Indic languages
['id', 'ind'], // Indonesian
['', 'ine'], // Indo-European languages
['', 'inh'], // Ingush
['ik', 'ipk'], // Inupiaq
['', 'ira'], // Iranian languages
['', 'iro'], // Iroquoian languages
['it', 'ita'], // Italian
['jv', 'jav'], // Javanese
['', 'jbo'], // Lojban
['ja', 'jpn'], // Japanese
['', 'jpr'], // Judeo-Persian
['', 'jrb'], // Judeo-Arabic
['', 'kaa'], // Kara-Kalpak
['', 'kab'], // Kabyle
['', 'kac'], // Kachin; Jingpho
['kl', 'kal'], // Kalaallisut; Greenlandic
['', 'kam'], // Kamba
['kn', 'kan'], // Kannada
['', 'kar'], // Karen languages
['ks', 'kas'], // Kashmiri
['kr', 'kau'], // Kanuri
['', 'kaw'], // Kawi
['kk', 'kaz'], // Kazakh
['', 'kbd'], // Kabardian
['', 'kha'], // Khasi
['', 'khi'], // Khoisan languages
['km', 'khm'], // Central Khmer
['', 'kho'], // Khotanese; Sakan
['ki', 'kik'], // Kikuyu; Gikuyu
['rw', 'kin'], // Kinyarwanda
['ky', 'kir'], // Kirghiz; Kyrgyz
['', 'kmb'], // Kimbundu
['', 'kok'], // Konkani
['kv', 'kom'], // Komi
['kg', 'kon'], // Kongo
['ko', 'kor'], // Korean
['', 'kos'], // Kosraean
['', 'kpe'], // Kpelle
['', 'krc'], // Karachay-Balkar
['', 'krl'], // Karelian
['', 'kro'], // Kru languages
['', 'kru'], // Kurukh
['kj', 'kua'], // Kuanyama; Kwanyama
['', 'kum'], // Kumyk
['ku', 'kur'], // Kurdish
['', 'kut'], // Kutenai
['', 'lad'], // Ladino
['', 'lah'], // Lahnda
['', 'lam'], // Lamba
['lo', 'lao'], // Lao
['la', 'lat'], // Latin
['lv', 'lav'], // Latvian
['', 'lez'], // Lezghian
['li', 'lim'], // Limburgan; Limburger; Limburgish
['ln', 'lin'], // Lingala
['lt', 'lit'], // Lithuanian
['', 'lol'], // Mongo
['', 'loz'], // Lozi
['lb', 'ltz'], // Luxembourgish; Letzeburgesch
['', 'lua'], // Luba-Lulua
['lu', 'lub'], // Luba-Katanga
['lg', 'lug'], // Ganda
['', 'lui'], // Luiseno
['', 'lun'], // Lunda
['', 'luo'], // Luo (Kenya and Tanzania)
['', 'lus'], // Lushai
['mk', 'mac'], // Macedonian
['', 'mad'], // Madurese
['', 'mag'], // Magahi
['mh', 'mah'], // Marshallese
['', 'mai'], // Maithili
['', 'mak'], // Makasar
['ml', 'mal'], // Malayalam
['', 'man'], // Mandingo
['mi', 'mao'], // Maori
['', 'map'], // Austronesian languages
['mr', 'mar'], // Marathi
['', 'mas'], // Masai
['ms', 'may'], // Malay
['', 'mdf'], // Moksha
['', 'mdr'], // Mandar
['', 'men'], // Mende
['', 'mga'], // Irish, Middle (900-1200)
['', 'mic'], // Mi'kmaq; Micmac
['', 'min'], // Minangkabau
['', 'mis'], // Uncoded languages
['', 'mkh'], // Mon-Khmer languages
['mg', 'mlg'], // Malagasy
['mt', 'mlt'], // Maltese
['', 'mnc'], // Manchu
['', 'mni'], // Manipuri
['', 'mno'], // Manobo languages
['', 'moh'], // Mohawk
['mn', 'mon'], // Mongolian
['', 'mos'], // Mossi
['', 'mul'], // Multiple languages
['', 'mun'], // Munda languages
['', 'mus'], // Creek
['', 'mwl'], // Mirandese
['', 'mwr'], // Marwari
['', 'myn'], // Mayan languages
['', 'myv'], // Erzya
['', 'nah'], // Nahuatl languages
['', 'nai'], // North American Indian languages
['', 'nap'], // Neapolitan
['na', 'nau'], // Nauru
['nv', 'nav'], // Navajo; Navaho
['nr', 'nbl'], // Ndebele, South; South Ndebele
['nd', 'nde'], // Ndebele, North; North Ndebele
['ng', 'ndo'], // Ndonga
['', 'nds'], // Low German; Low Saxon; German, Low; Saxon, Low
['ne', 'nep'], // Nepali
['', 'new'], // Nepal Bhasa; Newari
['', 'nia'], // Nias
['', 'nic'], // Niger-Kordofanian languages
['', 'niu'], // Niuean
['nn', 'nno'], // Norwegian Nynorsk; Nynorsk, Norwegian
['nb', 'nob'], // Bokmål, Norwegian; Norwegian Bokmål
['', 'nog'], // Nogai
['', 'non'], // Norse, Old
['no', 'nor'], // Norwegian
['', 'nqo'], // N'Ko
['', 'nso'], // Pedi; Sepedi; Northern Sotho
['', 'nub'], // Nubian languages
['', 'nwc'], // Classical Newari; Old Newari; Classical Nepal Bhasa
['ny', 'nya'], // Chichewa; Chewa; Nyanja
['', 'nym'], // Nyamwezi
['', 'nyn'], // Nyankole
['', 'nyo'], // Nyoro
['', 'nzi'], // Nzima
['oc', 'oci'], // Occitan (post 1500)
['oj', 'oji'], // Ojibwa
['or', 'ori'], // Oriya
['om', 'orm'], // Oromo
['', 'osa'], // Osage
['os', 'oss'], // Ossetian; Ossetic
['', 'ota'], // Turkish, Ottoman (1500-1928)
['', 'oto'], // Otomian languages
['', 'paa'], // Papuan languages
['', 'pag'], // Pangasinan
['', 'pal'], // Pahlavi
['', 'pam'], // Pampanga; Kapampangan
['pa', 'pan'], // Panjabi; Punjabi
['', 'pap'], // Papiamento
['', 'pau'], // Palauan
['', 'peo'], // Persian, Old (ca.600-400 B.C.)
['fa', 'per'], // Persian
['', 'phi'], // Philippine languages
['', 'phn'], // Phoenician
['pi', 'pli'], // Pali
['pl', 'pol'], // Polish
['', 'pon'], // Pohnpeian
['pt', 'por'], // Portuguese
['', 'pra'], // Prakrit languages
['', 'pro'], // Provençal, Old (to 1500); Occitan, Old (to 1500)
['ps', 'pus'], // Pushto; Pashto
['', 'qaaqtz'], // Reserved for local use
['qu', 'que'], // Quechua
['', 'raj'], // Rajasthani
['', 'rap'], // Rapanui
['', 'rar'], // Rarotongan; Cook Islands Maori
['', 'roa'], // Romance languages
['rm', 'roh'], // Romansh
['', 'rom'], // Romany
['ro', 'rum'], // Romanian; Moldavian; Moldovan
['rn', 'run'], // Rundi
['', 'rup'], // Aromanian; Arumanian; Macedo-Romanian
['ru', 'rus'], // Russian
['', 'sad'], // Sandawe
['sg', 'sag'], // Sango
['', 'sah'], // Yakut
['', 'sai'], // South American Indian languages
['', 'sal'], // Salishan languages
['', 'sam'], // Samaritan Aramaic
['sa', 'san'], // Sanskrit
['', 'sas'], // Sasak
['', 'sat'], // Santali
['', 'scn'], // Sicilian
['', 'sco'], // Scots
['', 'sel'], // Selkup
['', 'sem'], // Semitic languages
['', 'sga'], // Irish, Old (to 900)
['', 'sgn'], // Sign Languages
['', 'shn'], // Shan
['', 'sid'], // Sidamo
['si', 'sin'], // Sinhala; Sinhalese
['', 'sio'], // Siouan languages
['', 'sit'], // Sino-Tibetan languages
['', 'sla'], // Slavic languages
['sk', 'slo'], // Slovak
['sl', 'slv'], // Slovenian
['', 'sma'], // Southern Sami
['se', 'sme'], // Northern Sami
['', 'smi'], // Sami languages
['', 'smj'], // Lule Sami
['', 'smn'], // Inari Sami
['sm', 'smo'], // Samoan
['', 'sms'], // Skolt Sami
['sn', 'sna'], // Shona
['sd', 'snd'], // Sindhi
['', 'snk'], // Soninke
['', 'sog'], // Sogdian
['so', 'som'], // Somali
['', 'son'], // Songhai languages
['st', 'sot'], // Sotho, Southern
['es', 'spa'], // Spanish; Castilian
['sc', 'srd'], // Sardinian
['', 'srn'], // Sranan Tongo
['sr', 'srp'], // Serbian
['', 'srr'], // Serer
['', 'ssa'], // Nilo-Saharan languages
['ss', 'ssw'], // Swati
['', 'suk'], // Sukuma
['su', 'sun'], // Sundanese
['', 'sus'], // Susu
['', 'sux'], // Sumerian
['sw', 'swa'], // Swahili
['sv', 'swe'], // Swedish
['', 'syc'], // Classical Syriac
['', 'syr'], // Syriac
['ty', 'tah'], // Tahitian
['', 'tai'], // Tai languages
['ta', 'tam'], // Tamil
['tt', 'tat'], // Tatar
['te', 'tel'], // Telugu
['', 'tem'], // Timne
['', 'ter'], // Tereno
['', 'tet'], // Tetum
['tg', 'tgk'], // Tajik
['tl', 'tgl'], // Tagalog
['th', 'tha'], // Thai
['bo', 'tib'], // Tibetan
['', 'tig'], // Tigre
['ti', 'tir'], // Tigrinya
['', 'tiv'], // Tiv
['', 'tkl'], // Tokelau
['', 'tlh'], // Klingon; tlhIngan-Hol
['', 'tli'], // Tlingit
['', 'tmh'], // Tamashek
['', 'tog'], // Tonga (Nyasa)
['to', 'ton'], // Tonga (Tonga Islands)
['', 'tpi'], // Tok Pisin
['', 'tsi'], // Tsimshian
['tn', 'tsn'], // Tswana
['ts', 'tso'], // Tsonga
['tk', 'tuk'], // Turkmen
['', 'tum'], // Tumbuka
['', 'tup'], // Tupi languages
['tr', 'tur'], // Turkish
['', 'tut'], // Altaic languages
['', 'tvl'], // Tuvalu
['tw', 'twi'], // Twi
['', 'tyv'], // Tuvinian
['', 'udm'], // Udmurt
['', 'uga'], // Ugaritic
['ug', 'uig'], // Uighur; Uyghur
['uk', 'ukr'], // Ukrainian
['', 'umb'], // Umbundu
['', 'und'], // Undetermined
['ur', 'urd'], // Urdu
['uz', 'uzb'], // Uzbek
['', 'vai'], // Vai
['ve', 'ven'], // Venda
['vi', 'vie'], // Vietnamese
['vo', 'vol'], // Volapük
['', 'vot'], // Votic
['', 'wak'], // Wakashan languages
['', 'wal'], // Wolaitta; Wolaytta
['', 'war'], // Waray
['', 'was'], // Washo
['cy', 'wel'], // Welsh
['', 'wen'], // Sorbian languages
['wa', 'wln'], // Walloon
['wo', 'wol'], // Wolof
['', 'xal'], // Kalmyk; Oirat
['xh', 'xho'], // Xhosa
['', 'yao'], // Yao
['', 'yap'], // Yapese
['yi', 'yid'], // Yiddish
['yo', 'yor'], // Yoruba
['', 'ypk'], // Yupik languages
['', 'zap'], // Zapotec
['', 'zbl'], // Blissymbols; Blissymbolics; Bliss
['', 'zen'], // Zenaga
['', 'zgh'], // Standard Moroccan Tamazight
['za', 'zha'], // Zhuang; Chuang
['', 'znd'], // Zande languages
['zu', 'zul'], // Zulu
['', 'zun'], // Zuni
['', 'zxx'], // No linguistic content; Not applicable
// phpcs:enable Squiz.PHP.CommentedOutCode.Found
];
/**
* Initializes the rule defining the ISO 639 set.
*
* @throws ComponentException
*/
public function __construct(string $set = self::ALPHA2)
{
$index = array_search($set, self::AVAILABLE_SETS, true);
if ($index === false) {
throw new ComponentException(sprintf('"%s" is not a valid language set for ISO 639', $set));
}
parent::__construct(new In($this->getHaystack($index), true), ['set' => $set]);
}
/**
* @return string[]
*/
private function getHaystack(int $index): array
{
return array_filter(array_column(self::LANGUAGE_CODES, $index));
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use DateTimeImmutable;
use DateTimeInterface;
use function is_scalar;
/**
* Validates if a date is leap.
*
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jayson Reis <santosdosreis@gmail.com>
*/
final class LeapDate extends AbstractRule
{
/**
* @var string
*/
private $format;
/**
* Initializes the rule with the expected format.
*/
public function __construct(string $format)
{
$this->format = $format;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($input instanceof DateTimeInterface) {
return $input->format('m-d') === '02-29';
}
if (is_scalar($input)) {
return $this->validate(DateTimeImmutable::createFromFormat($this->format, (string) $input));
}
return false;
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use DateTimeInterface;
use function date;
use function is_numeric;
use function is_scalar;
use function sprintf;
use function strtotime;
/**
* Validates if a year is leap.
*
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jayson Reis <santosdosreis@gmail.com>
*/
final class LeapYear extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (is_numeric($input)) {
$date = strtotime(sprintf('%d-02-29', (int) $input));
return (bool) date('L', (int) $date);
}
if (is_scalar($input)) {
return $this->validate((int) date('Y', (int) strtotime((string) $input)));
}
if ($input instanceof DateTimeInterface) {
return $this->validate($input->format('Y'));
}
return false;
}
}
+130
View File
@@ -0,0 +1,130 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Countable as CountableInterface;
use Respect\Validation\Exceptions\ComponentException;
use function count;
use function get_object_vars;
use function is_array;
use function is_int;
use function is_object;
use function is_string;
use function mb_strlen;
use function sprintf;
/**
* Validates the length of the given input.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Blake Hair <blake.hair@gmail.com>
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Hugo Hamon <hugo.hamon@sensiolabs.com>
* @author João Torquato <joao.otl@gmail.com>
* @author Marcelo Araujo <msaraujo@php.net>
*/
final class Length extends AbstractRule
{
/**
* @var int|null
*/
private $minValue;
/**
* @var int|null
*/
private $maxValue;
/**
* @var bool
*/
private $inclusive;
/**
* Creates the rule with a minimum and maximum value.
*
* @throws ComponentException
*/
public function __construct(?int $min = null, ?int $max = null, bool $inclusive = true)
{
$this->minValue = $min;
$this->maxValue = $max;
$this->inclusive = $inclusive;
if ($max !== null && $min > $max) {
throw new ComponentException(sprintf('%d cannot be less than %d for validation', $min, $max));
}
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
$length = $this->extractLength($input);
if ($length === null) {
return false;
}
return $this->validateMin($length) && $this->validateMax($length);
}
/**
* @param mixed $input
*/
private function extractLength($input): ?int
{
if (is_string($input)) {
return (int) mb_strlen($input);
}
if (is_array($input) || $input instanceof CountableInterface) {
return count($input);
}
if (is_object($input)) {
return $this->extractLength(get_object_vars($input));
}
if (is_int($input)) {
return $this->extractLength((string) $input);
}
return null;
}
private function validateMin(int $length): bool
{
if ($this->minValue === null) {
return true;
}
if ($this->inclusive) {
return $length >= $this->minValue;
}
return $length > $this->minValue;
}
private function validateMax(int $length): bool
{
if ($this->maxValue === null) {
return true;
}
if ($this->inclusive) {
return $length <= $this->maxValue;
}
return $length < $this->maxValue;
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates whether the input is less than a value.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class LessThan extends AbstractComparison
{
/**
* {@inheritDoc}
*/
protected function compare($left, $right): bool
{
return $left < $right;
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_string;
use function mb_strtolower;
/**
* Validates whether the characters in the input are lowercase.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jean Pimentel <jeanfap@gmail.com>
*/
final class Lowercase extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_string($input)) {
return false;
}
return $input === mb_strtolower($input);
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function array_map;
use function count;
use function str_split;
/**
* Validate whether a given input is a Luhn number.
*
* @see https://en.wikipedia.org/wiki/Luhn_algorithm
*
* @author Alexander Gorshkov <mazanax@yandex.ru>
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Luhn extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!(new Digit())->validate($input)) {
return false;
}
return $this->isValid((string) $input);
}
private function isValid(string $input): bool
{
$sum = 0;
$digits = array_map('intval', str_split($input));
$numDigits = count($digits);
$parity = $numDigits % 2;
for ($i = 0; $i < $numDigits; ++$i) {
$digit = $digits[$i];
if ($parity == $i % 2) {
$digit <<= 1;
if (9 < $digit) {
$digit = $digit - 9;
}
}
$sum += $digit;
}
return $sum % 10 == 0;
}
}
@@ -0,0 +1,36 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_string;
use function preg_match;
/**
* Validates whether the input is a valid MAC address.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Fábio da Silva Ribeiro <fabiorphp@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class MacAddress extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_string($input)) {
return false;
}
return preg_match('/^(([0-9a-fA-F]{2}-){5}|([0-9a-fA-F]{2}:){5})[0-9a-fA-F]{2}$/', $input) > 0;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates whether the input is less than or equal to a value.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Max extends AbstractComparison
{
/**
* {@inheritDoc}
*/
protected function compare($left, $right): bool
{
return $left <= $right;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates a maximum age for a given date.
*
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class MaxAge extends AbstractAge
{
/**
* {@inheritDoc}
*/
protected function compare(int $baseDate, int $givenDate): bool
{
return $baseDate <= $givenDate;
}
}
+66
View File
@@ -0,0 +1,66 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use finfo;
use SplFileInfo;
use function is_file;
use function is_string;
use const FILEINFO_MIME_TYPE;
/**
* Validates if the input is a file and if its MIME type matches the expected one.
*
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Mimetype extends AbstractRule
{
/**
* @var string
*/
private $mimetype;
/**
* @var finfo
*/
private $fileInfo;
/**
* Initializes the rule by defining the expected mimetype from the input.
*/
public function __construct(string $mimetype, ?finfo $fileInfo = null)
{
$this->mimetype = $mimetype;
$this->fileInfo = $fileInfo ?: new finfo();
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($input instanceof SplFileInfo) {
return $this->validate($input->getPathname());
}
if (!is_string($input)) {
return false;
}
if (!is_file($input)) {
return false;
}
return $this->mimetype === $this->fileInfo->file($input, FILEINFO_MIME_TYPE);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates whether the input is greater than or equal to a value.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class Min extends AbstractComparison
{
/**
* {@inheritDoc}
*/
protected function compare($left, $right): bool
{
return $left >= $right;
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* Validates a minimum age for a given date.
*
* @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jean Pimentel <jeanfap@gmail.com>
* @author Kennedy Tedesco <kennedyt.tw@gmail.com>
*/
final class MinAge extends AbstractAge
{
/**
* {@inheritDoc}
*/
protected function compare(int $baseDate, int $givenDate): bool
{
return $baseDate >= $givenDate;
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Respect\Validation\Rules;
use libphonenumber\NumberParseException;
use function is_scalar;
final class Mobile extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_scalar($input)) {
return false;
}
try {
// 此处使用简单的正则表达式检查中国大陆的手机号码格式
return preg_match('/^1[3456789]\d{9}$/', (string) $input) === 1;
} catch (NumberParseException $e) {
return false;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
/**
* @author Danilo Benevides <danilobenevides01@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Jean Pimentel <jeanfap@gmail.com>
*/
final class Multiple extends AbstractRule
{
/**
* @var int
*/
private $multipleOf;
public function __construct(int $multipleOf)
{
$this->multipleOf = $multipleOf;
}
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if ($this->multipleOf == 0) {
return $input == 0;
}
return $input % $this->multipleOf == 0;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function is_numeric;
/**
* Validates whether the input is a negative number.
*
* @author Alexandre Gomes Gaigalas <alganet@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Ismael Elias <ismael.esq@hotmail.com>
*/
final class Negative extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_numeric($input)) {
return false;
}
return $input < 0;
}
}
@@ -0,0 +1,56 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function array_map;
use function floor;
use function mb_strlen;
use function str_split;
/**
* Validates the access key of the Brazilian electronic invoice (NFe).
*
*
* (pt-br) Valida chave de acesso de NFe, mais especificamente, relacionada ao DANFE.
*
* @see (pt-br) Manual de Integração do Contribuinte v4.0.1 em http://www.nfe.fazenda.gov.br
*
* @author Andrey Knupp Vital <andreykvital@gmail.com>
* @author Danilo Correa <danilosilva87@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class NfeAccessKey extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (mb_strlen($input) !== 44) {
return false;
}
$digits = array_map('intval', str_split($input));
$w = [];
for ($i = 0, $z = 5, $m = 43; $i <= $m; ++$i) {
$z = $i < $m ? $z - 1 == 1 ? 9 : $z - 1 : 0;
$w[] = $z;
}
for ($i = 0, $s = 0, $k = 44; $i < $k; ++$i) {
$s += $digits[$i] * $w[$i];
}
$s -= 11 * floor($s / 11);
$v = $s == 0 || $s == 1 ? 0 : 11 - $s;
return $v == $digits[43];
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function array_pop;
use function array_sum;
use function is_numeric;
use function is_string;
use function mb_substr;
use function preg_match;
use function str_split;
/**
* Validates Spain's fiscal identification number (NIF).
*
*
* @see https://es.wikipedia.org/wiki/N%C3%BAmero_de_identificaci%C3%B3n_fiscal
*
* @author Henrique Moody <henriquemoody@gmail.com>
* @author Julián Gutiérrez <juliangut@gmail.com>
* @author Senén <senen@instasent.com>
*/
final class Nif extends AbstractRule
{
/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_string($input)) {
return false;
}
if (preg_match('/^(\d{8})([A-Z])$/', $input, $matches)) {
return $this->validateDni((int) $matches[1], $matches[2]);
}
if (preg_match('/^([KLMXYZ])(\d{7})([A-Z])$/', $input, $matches)) {
return $this->validateNie($matches[1], $matches[2], $matches[3]);
}
if (preg_match('/^([A-HJNP-SUVW])(\d{7})([0-9A-Z])$/', $input, $matches)) {
return $this->validateCif($matches[2], $matches[3]);
}
return false;
}
private function validateDni(int $number, string $control): bool
{
return mb_substr('TRWAGMYFPDXBNJZSQVHLCKE', $number % 23, 1) === $control;
}
private function validateNie(string $prefix, string $number, string $control): bool
{
if ($prefix === 'Y') {
return $this->validateDni((int) ('1' . $number), $control);
}
if ($prefix === 'Z') {
return $this->validateDni((int) ('2' . $number), $control);
}
return $this->validateDni((int) $number, $control);
}
private function validateCif(string $number, string $control): bool
{
$code = 0;
$position = 1;
/** @var int $digit */
foreach (str_split($number) as $digit) {
$increaser = $digit;
if ($position % 2 !== 0) {
$increaser = array_sum(str_split((string) ($digit * 2)));
}
$code += $increaser;
++$position;
}
$digits = str_split((string) $code);
$lastDigit = (int) array_pop($digits);
$key = $lastDigit === 0 ? 0 : 10 - $lastDigit;
if (is_numeric($control)) {
return (int) $key === (int) $control;
}
return mb_substr('JABCDEFGHI', $key % 10, 1) === $control;
}
}

Some files were not shown because too many files have changed in this diff Show More