Remove previous validation engine

After many refactorings, no rules use the previous validation engine.
That means we can remove the unused code from the repository and switch
from the previous to the new validation engine everywhere.

This commit will also soft deprecate the methods "validate()", and
"check()" in all the rules and the "assert()" in all rules but the
Validator itself. That means using those methods will still be allowed,
but static analysis tools might complain.

This is a big step toward releasing the next major version, as the code
is pretty much the way it should be when I release the next version.
There's some documentation to be updated, and I would like to change the
behavior of a couple of rules.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
Henrique Moody 2024-03-25 09:18:05 +01:00
parent ae369c4791
commit 66faefd695
No known key found for this signature in database
GPG key ID: 221E9281655813A6
113 changed files with 263 additions and 1092 deletions

View file

@ -42,7 +42,7 @@ create a validator that validates if a string is equal to "Hello World".
### Creating the rule
The rule itself needs to implement the `Validatable` interface but, it is
convenient to just extend the `AbstractRule` class.
convenient to just extend the `Simple` or `Standard` class.
Doing that, you'll only need to declare one method: `validate($input)`.
This method must return `true` or `false`.
@ -62,14 +62,15 @@ declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Message\Template;
use Respect\Validation\Rules\Core\Simple;
#[Template(
'{{name}} must be a Hello World',
'{{name}} must not be a Hello World',
)]
final class HelloWorld extends AbstractRule
final class HelloWorld extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return $input === 'Hello World';
}

View file

@ -3,23 +3,24 @@
You can also create and use your own rules. To do this, you will need to create
a rule and an exception to go with the rule.
To create a rule, you need to create a class that extends the AbstractRule class
and is within the Rules `namespace`. When the rule is called the logic inside the
validate method will be executed. Here's how the class should look:
To create a rule, you need to create a class that implements the `Validatable` interface
and is within the Rules `namespace`. It is convenient to just extend the `Simple` or
`Standard` class. When the rule is called the logic inside the validate method will be
executed. Here's how the class should look:
```php
namespace My\Validation\Rules;
use Respect\Validation\Rules\AbstractRule;
use Respect\Validation\Message\Template;
use Respect\Validation\Rules\Core\Simple;
#[Template(
'{{name}} is something',
'{{name}} is not something',
)]
final class Something extends AbstractRule
final class Something extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
// Do something here with the $input and return a boolean value
}

View file

@ -10,106 +10,26 @@ declare(strict_types=1);
namespace Respect\Validation\Exceptions;
use InvalidArgumentException;
use Respect\Validation\Message\Template;
use Respect\Validation\Message\TemplateRenderer;
use function count;
use function preg_match;
class ValidationException extends InvalidArgumentException implements Exception
final class ValidationException extends InvalidArgumentException implements Exception
{
public const MODE_DEFAULT = 'default';
public const MODE_NEGATIVE = 'negative';
private string $mode = self::MODE_DEFAULT;
/**
* @var array<Template>
*/
private readonly array $templates;
/**
* @param array<string, mixed> $params
* @param array<Template> $templates
*/
/** @param array<string, mixed> $messages */
public function __construct(
private readonly mixed $input,
private readonly string $id,
private array $params,
private string $template,
array $templates,
private readonly TemplateRenderer $formatter
string $message,
private readonly string $fullMessage,
private readonly array $messages,
) {
if (count($templates) === 0) {
$templates = [new Template('{{name}} must be valid', '{{name}} must not be valid')];
}
$this->templates = $templates;
parent::__construct($this->createMessage());
parent::__construct($message);
}
public function getId(): string
public function getFullMessage(): string
{
return $this->id;
return $this->fullMessage;
}
/**
* @return mixed[]
*/
public function getParams(): array
/** @return array<string, mixed> */
public function getMessages(): array
{
return $this->params;
}
public function getParam(string $name): mixed
{
return $this->params[$name] ?? null;
}
public function updateMode(string $mode): void
{
$this->mode = $mode;
$this->message = $this->createMessage();
}
public function updateTemplate(string $template): void
{
$this->template = $template;
$this->message = $this->createMessage();
}
/**
* @param mixed[] $params
*/
public function updateParams(array $params): void
{
$this->params = $params;
$this->message = $this->createMessage();
}
public function hasCustomTemplate(): bool
{
return preg_match('/__[a-z_]+_/', $this->template) === 0;
}
private function getTemplateString(): string
{
foreach ($this->templates as $template) {
if ($template->id === $this->template) {
return $template->{$this->mode};
}
}
return $this->template;
}
private function createMessage(): string
{
return $this->formatter->render($this->getTemplateString(), $this->input, $this->params);
}
public function __toString(): string
{
return $this->getMessage();
return $this->messages;
}
}

View file

@ -1,33 +0,0 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Exceptions;
final class ValidatorException extends \Exception implements Exception
{
/** @param array<string, mixed> $messages */
public function __construct(
string $message,
private readonly string $fullMessage,
private readonly array $messages,
) {
parent::__construct($message);
}
public function getFullMessage(): string
{
return $this->fullMessage;
}
/** @return array<string, mixed> */
public function getMessages(): array
{
return $this->messages;
}
}

View file

@ -11,16 +11,12 @@ namespace Respect\Validation;
use ReflectionClass;
use ReflectionException;
use ReflectionObject;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Exceptions\InvalidClassException;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Message\Parameter\Processor;
use Respect\Validation\Message\Parameter\Raw;
use Respect\Validation\Message\Parameter\Stringify;
use Respect\Validation\Message\Parameter\Trans;
use Respect\Validation\Message\TemplateCollector;
use Respect\Validation\Message\TemplateRenderer;
use Respect\Validation\Transformers\DeprecatedAttribute;
use Respect\Validation\Transformers\DeprecatedKey;
use Respect\Validation\Transformers\DeprecatedKeyNested;
@ -33,7 +29,6 @@ use Respect\Validation\Transformers\RuleSpec;
use Respect\Validation\Transformers\Transformer;
use function array_merge;
use function lcfirst;
use function sprintf;
use function trim;
use function ucfirst;
@ -52,8 +47,6 @@ final class Factory
private Processor $processor;
private TemplateCollector $templateCollector;
private Transformer $transformer;
private static Factory $defaultInstance;
@ -62,7 +55,6 @@ final class Factory
{
$this->translator = static fn (string $message) => $message;
$this->processor = new Raw(new Trans($this->translator, new Stringify()));
$this->templateCollector = new TemplateCollector();
$this->transformer = new DeprecatedAttribute(
new DeprecatedKey(
new DeprecatedKeyValue(
@ -126,26 +118,6 @@ final class Factory
return $this->createRuleSpec($this->transformer->transform(new RuleSpec($ruleName, $arguments)));
}
/**
* @param mixed[] $extraParams
*/
public function exception(Validatable $validatable, mixed $input, array $extraParams = []): ValidationException
{
$reflection = new ReflectionObject($validatable);
$params = ['input' => $input] + $extraParams + $validatable->getParams();
$id = lcfirst($reflection->getShortName());
if ($validatable->getName() !== null) {
$id = $params['name'] = $validatable->getName();
}
$standardTemplate = $reflection->getMethod('getStandardTemplate');
$template = $validatable->getTemplate() ?? $standardTemplate->invoke($validatable, $input);
$templates = $this->templateCollector->extract($validatable);
$formatter = new TemplateRenderer($this->translator, $this->processor);
return new ValidationException($input, $id, $params, $template, $templates, $formatter);
}
public static function setDefaultInstance(self $defaultInstance): void
{
self::$defaultInstance = $defaultInstance;
@ -170,12 +142,18 @@ final class Factory
try {
/** @var class-string<Validatable> $name */
$name = $namespace . '\\' . ucfirst($ruleName);
/** @var Validatable $rule */
$rule = $this
->createReflectionClass($name, Validatable::class)
->newInstanceArgs($arguments);
$reflection = new ReflectionClass($name);
if (!$reflection->isSubclassOf(Validatable::class)) {
throw new InvalidClassException(
sprintf('"%s" must be an instance of "%s"', $name, Validatable::class)
);
}
return $rule;
if (!$reflection->isInstantiable()) {
throw new InvalidClassException(sprintf('"%s" must be instantiable', $name));
}
return $reflection->newInstanceArgs($arguments);
} catch (ReflectionException) {
continue;
}
@ -183,24 +161,4 @@ final class Factory
throw new ComponentException(sprintf('"%s" is not a valid rule name', $ruleName));
}
/**
* @param class-string $name
* @param class-string $parentName
*
* @return ReflectionClass<ValidationException|Validatable|object>
*/
private function createReflectionClass(string $name, string $parentName): ReflectionClass
{
$reflection = new ReflectionClass($name);
if (!$reflection->isSubclassOf($parentName) && $parentName !== $name) {
throw new InvalidClassException(sprintf('"%s" must be an instance of "%s"', $name, $parentName));
}
if (!$reflection->isInstantiable()) {
throw new InvalidClassException(sprintf('"%s" must be instantiable', $name));
}
return $reflection;
}
}

View file

@ -9,62 +9,31 @@ declare(strict_types=1);
namespace Respect\Validation\Helpers;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Message\Parameter\Stringify;
use Respect\Validation\Message\TemplateRenderer;
use function sprintf;
use function trigger_error;
use const E_USER_DEPRECATED;
use Respect\Validation\Validator;
trait DeprecatedValidatableMethods
{
/**
* @deprecated Calling `validate()` directly is deprecated, please use the `Validator::isValid()` class instead.
*/
public function validate(mixed $input): bool
{
$this->triggerDeprecation(__METHOD__);
return false;
return $this->evaluate($input)->isValid;
}
/**
* @deprecated Calling `assert()` directly is deprecated, please use the `Validator::assert()` instead.
*/
public function assert(mixed $input): void
{
$this->triggerDeprecation(__FUNCTION__);
Validator::create($this)->assert($input);
}
/**
* @deprecated Calling `check()` directly is deprecated, please use the `Validator::assert()` instead.
*/
public function check(mixed $input): void
{
$this->triggerDeprecation(__FUNCTION__);
}
/** @param array<string, mixed> $extraParameters */
public function reportError(mixed $input, array $extraParameters = []): ValidationException
{
$this->triggerDeprecation(__FUNCTION__);
return new ValidationException(
input: $input,
id: 'id',
params: $extraParameters,
template: 'template',
templates: [],
formatter: new TemplateRenderer(static fn (string $message) => $message, new Stringify()),
);
}
/** @return array<string, mixed> */
public function getParams(): array
{
$this->triggerDeprecation(__FUNCTION__);
return [];
}
private function triggerDeprecation(string $function): void
{
trigger_error(
sprintf('The "%s" method is deprecated, please use the "Validator" class instead.', $function),
E_USER_DEPRECATED
);
Validator::create($this)->assert($input);
}
}

View file

@ -14,7 +14,7 @@ use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Message\Parameter\Processor;
use Respect\Validation\Mode;
use Respect\Validation\Result;
use Respect\Validation\Rule;
use Respect\Validation\Validatable;
use Throwable;
use function call_user_func;
@ -71,7 +71,7 @@ final class StandardRenderer implements Renderer
}
/** @return array<Template> */
private function extractTemplates(Rule $rule): array
private function extractTemplates(Validatable $rule): array
{
if (!isset($this->templates[$rule::class])) {
$reflection = new ReflectionClass($rule);

View file

@ -1,33 +0,0 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Message;
use ReflectionClass;
final class TemplateCollector
{
/** @var array<string, array<Template>> */
private array $templates = [];
/**
* @return array<Template>
*/
public function extract(object $object): array
{
if (!isset($this->templates[$object::class])) {
$reflection = new ReflectionClass($object);
foreach ($reflection->getAttributes(Template::class) as $attribute) {
$this->templates[$object::class][] = $attribute->newInstance();
}
}
return $this->templates[$object::class] ?? [];
}
}

View file

@ -1,60 +0,0 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Message;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Message\Parameter\Processor;
use Throwable;
use function call_user_func;
use function preg_replace_callback;
use function sprintf;
final class TemplateRenderer
{
/** @var callable */
private $translator;
public function __construct(
callable $translator,
private readonly Processor $processor
) {
$this->translator = $translator;
}
/**
* @param mixed[] $parameters
*/
public function render(string $template, mixed $input, array $parameters): string
{
$parameters['name'] ??= $this->processor->process('input', $input);
return (string) preg_replace_callback(
'/{{(\w+)(\|([^}]+))?}}/',
function (array $matches) use ($parameters) {
if (!isset($parameters[$matches[1]])) {
return $matches[0];
}
return $this->processor->process($matches[1], $parameters[$matches[1]], $matches[3] ?? null);
},
$this->translate($template)
);
}
private function translate(mixed $message): string
{
try {
return call_user_func($this->translator, (string) $message);
} catch (Throwable $throwable) {
throw new ComponentException(sprintf('Failed to translate "%s"', $message), 0, $throwable);
}
}
}

View file

@ -32,9 +32,9 @@ final class Result
public function __construct(
public readonly bool $isValid,
public readonly mixed $input,
public readonly Rule $rule,
public readonly Validatable $rule,
public readonly array $parameters = [],
string $template = Rule::TEMPLATE_STANDARD,
string $template = Validatable::TEMPLATE_STANDARD,
public readonly Mode $mode = Mode::DEFAULT,
?string $name = null,
?string $id = null,
@ -50,9 +50,9 @@ final class Result
/** @param array<string, mixed> $parameters */
public static function failed(
mixed $input,
Rule $rule,
Validatable $rule,
array $parameters = [],
string $template = Rule::TEMPLATE_STANDARD
string $template = Validatable::TEMPLATE_STANDARD
): self {
return new self(false, $input, $rule, $parameters, $template);
}
@ -60,9 +60,9 @@ final class Result
/** @param array<string, mixed> $parameters */
public static function passed(
mixed $input,
Rule $rule,
Validatable $rule,
array $parameters = [],
string $template = Rule::TEMPLATE_STANDARD
string $template = Validatable::TEMPLATE_STANDARD
): self {
return new self(true, $input, $rule, $parameters, $template);
}

View file

@ -1,25 +0,0 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation;
interface Rule
{
public const TEMPLATE_STANDARD = '__standard__';
public function evaluate(mixed $input): Result;
public function getName(): ?string;
public function setName(string $name): static;
public function getTemplate(): ?string;
public function setTemplate(string $template): static;
}

View file

@ -1,97 +0,0 @@
<?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\Result;
use Respect\Validation\Validatable;
abstract class AbstractRule implements Validatable
{
protected ?string $template = null;
private ?string $name = null;
public function assert(mixed $input): void
{
if ($this->validate($input)) {
return;
}
throw $this->reportError($input);
}
public function evaluate(mixed $input): Result
{
return new Result(
$this->validate($input),
$input,
$this,
$this->getParams(),
$this->getStandardTemplate($input)
);
}
public function check(mixed $input): void
{
$this->assert($input);
}
public function getName(): ?string
{
return $this->name;
}
/**
* @param mixed[] $extraParameters
*/
public function reportError(mixed $input, array $extraParameters = []): ValidationException
{
return Factory::getDefaultInstance()->exception($this, $input, $extraParameters);
}
public function setName(string $name): static
{
$this->name = $name;
return $this;
}
public function setTemplate(string $template): static
{
$this->template = $template;
return $this;
}
public function getTemplate(): ?string
{
return $this->template;
}
/**
* @return array<string, mixed>
*/
public function getParams(): array
{
return [];
}
protected function getStandardTemplate(mixed $input): string
{
return self::TEMPLATE_STANDARD;
}
public function __invoke(mixed $input): bool
{
return $this->validate($input);
}
}

View file

@ -11,8 +11,8 @@ namespace Respect\Validation\Rules;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Rule;
use Respect\Validation\Rules\Core\Composite;
use Respect\Validation\Validatable;
use function array_filter;
use function array_map;
@ -36,7 +36,7 @@ final class AllOf extends Composite
public function evaluate(mixed $input): Result
{
$children = array_map(static fn (Rule $rule) => $rule->evaluate($input), $this->rules);
$children = array_map(static fn (Validatable $rule) => $rule->evaluate($input), $this->rules);
$valid = array_reduce($children, static fn (bool $carry, Result $result) => $carry && $result->isValid, true);
$failed = array_filter($children, static fn (Result $result): bool => !$result->isValid);
$template = self::TEMPLATE_SOME;

View file

@ -26,7 +26,7 @@ final class AlwaysInvalid extends Simple
{
public const TEMPLATE_SIMPLE = '__simple__';
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return false;
}

View file

@ -18,7 +18,7 @@ use Respect\Validation\Rules\Core\Simple;
)]
final class AlwaysValid extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return true;
}

View file

@ -11,8 +11,8 @@ namespace Respect\Validation\Rules;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Rule;
use Respect\Validation\Rules\Core\Composite;
use Respect\Validation\Validatable;
use function array_map;
use function array_reduce;
@ -25,7 +25,7 @@ final class AnyOf extends Composite
{
public function evaluate(mixed $input): Result
{
$children = array_map(static fn (Rule $rule) => $rule->evaluate($input), $this->rules);
$children = array_map(static fn (Validatable $rule) => $rule->evaluate($input), $this->rules);
$valid = array_reduce($children, static fn (bool $carry, Result $result) => $carry || $result->isValid, false);
return (new Result($valid, $input, $this))->withChildren(...$children);

View file

@ -20,7 +20,7 @@ use function is_array;
)]
final class ArrayType extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_array($input);
}

View file

@ -22,7 +22,7 @@ use function is_array;
)]
final class ArrayVal extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_array($input) || $input instanceof ArrayAccess || $input instanceof SimpleXMLElement;
}

View file

@ -22,7 +22,7 @@ use function preg_match;
)]
final class Base64 extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -20,7 +20,7 @@ use function is_bool;
)]
final class BoolType extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_bool($input);
}

View file

@ -24,7 +24,7 @@ use const FILTER_VALIDATE_BOOLEAN;
)]
final class BoolVal extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_bool(filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE));
}

View file

@ -27,7 +27,7 @@ use function strval;
)]
final class Bsn extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;

View file

@ -20,7 +20,7 @@ use function is_callable;
)]
final class CallableType extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_callable($input);
}

View file

@ -38,7 +38,7 @@ final class Callback extends Simple
$this->arguments = $arguments;
}
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return (bool) call_user_func_array($this->callback, $this->getArguments($input));
}

View file

@ -22,7 +22,7 @@ use function preg_replace;
)]
final class Cnh extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;

View file

@ -25,7 +25,7 @@ use function str_split;
)]
final class Cnpj extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;

View file

@ -13,8 +13,10 @@ use Respect\Validation\Result;
abstract class Simple extends Standard
{
abstract protected function isValid(mixed $input): bool;
public function evaluate(mixed $input): Result
{
return new Result($this->validate($input), $input, $this);
return new Result($this->isValid($input), $input, $this);
}
}

View file

@ -21,7 +21,7 @@ use function is_array;
)]
final class Countable extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_array($input) || $input instanceof CountableInterface;
}

View file

@ -23,7 +23,7 @@ use function preg_replace;
)]
final class Cpf extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
// Code ported from jsfromhell.com
$c = preg_replace('/\D/', '', $input);

View file

@ -23,7 +23,7 @@ use function is_scalar;
)]
final class Directory extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if ($input instanceof SplFileInfo) {
return $input->isDir();

View file

@ -37,7 +37,7 @@ final class Email extends Simple
$this->validator = $validator;
}
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -22,7 +22,7 @@ use const FILTER_VALIDATE_INT;
)]
final class Even extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (filter_var($input, FILTER_VALIDATE_INT) === false) {
return false;

View file

@ -22,7 +22,7 @@ use function is_scalar;
)]
final class Executable extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if ($input instanceof SplFileInfo) {
return $input->isExecutable();

View file

@ -22,7 +22,7 @@ use function is_string;
)]
final class Exists extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if ($input instanceof SplFileInfo) {
$input = $input->getPathname();

View file

@ -23,7 +23,7 @@ use const FILTER_VALIDATE_BOOLEAN;
)]
final class FalseVal extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === false;
}

View file

@ -20,7 +20,7 @@ use function is_numeric;
)]
final class Fibonacci extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_numeric($input)) {
return false;

View file

@ -22,7 +22,7 @@ use function is_string;
)]
final class File extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if ($input instanceof SplFileInfo) {
return $input->isFile();

View file

@ -21,7 +21,7 @@ use function is_numeric;
)]
final class Finite extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_numeric($input) && is_finite((float) $input);
}

View file

@ -20,7 +20,7 @@ use function is_float;
)]
final class FloatType extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_float($input);
}

View file

@ -23,7 +23,7 @@ use const FILTER_VALIDATE_FLOAT;
)]
final class FloatVal extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_float(filter_var($input, FILTER_VALIDATE_FLOAT));
}

View file

@ -28,7 +28,7 @@ final class Hetu extends Simple
{
use CanValidateDateTime;
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -103,7 +103,7 @@ final class Iban extends Simple
'VG' => 24,
];
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -33,10 +33,10 @@ final class Image extends Simple
$this->fileInfo = $fileInfo ?: new finfo(FILEINFO_MIME_TYPE);
}
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if ($input instanceof SplFileInfo) {
return $this->validate($input->getPathname());
return $this->isValid($input->getPathname());
}
if (!is_string($input)) {

View file

@ -24,10 +24,7 @@ final class Imei extends Simple
{
private const IMEI_SIZE = 15;
/**
* @see https://en.wikipedia.org/wiki/International_Mobile_Station_Equipment_Identity
*/
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;
@ -38,6 +35,6 @@ final class Imei extends Simple
return false;
}
return (new Luhn())->validate($numbers);
return (new Luhn())->isValid($numbers);
}
}

View file

@ -21,7 +21,7 @@ use function is_numeric;
)]
final class Infinite extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_numeric($input) && is_infinite((float) $input);
}

View file

@ -20,7 +20,7 @@ use function is_int;
)]
final class IntType extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_int($input);
}

View file

@ -22,7 +22,7 @@ use function preg_match;
)]
final class IntVal extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (is_int($input)) {
return true;

View file

@ -32,7 +32,7 @@ final class Isbn extends Simple
'(?:97[89][- ]?)?[0-9]{1,5}[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9X]$',
];
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;

View file

@ -20,7 +20,7 @@ use function is_iterable;
)]
final class IterableType extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_iterable($input);
}

View file

@ -21,7 +21,7 @@ final class IterableVal extends Simple
{
use CanValidateIterable;
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return $this->isIterable($input);
}

View file

@ -26,7 +26,7 @@ use const JSON_ERROR_NONE;
)]
final class Json extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input) || $input === '') {
return false;

View file

@ -27,14 +27,14 @@ final class LeapDate extends Simple
) {
}
public function validate(mixed $input): bool
protected function isValid(mixed $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 $this->isValid(DateTimeImmutable::createFromFormat($this->format, (string) $input));
}
return false;

View file

@ -25,7 +25,7 @@ use function strtotime;
)]
final class LeapYear extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (is_numeric($input)) {
$date = strtotime(sprintf('%d-02-29', (int) $input));
@ -34,11 +34,11 @@ final class LeapYear extends Simple
}
if (is_scalar($input)) {
return $this->validate((int) date('Y', (int) strtotime((string) $input)));
return $this->isValid((int) date('Y', (int) strtotime((string) $input)));
}
if ($input instanceof DateTimeInterface) {
return $this->validate($input->format('Y'));
return $this->isValid($input->format('Y'));
}
return false;

View file

@ -21,7 +21,7 @@ use function mb_strtolower;
)]
final class Lowercase extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -25,19 +25,14 @@ use function str_split;
)]
final class Luhn extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!(new Digit())->evaluate($input)->isValid) {
return false;
}
return $this->isValid((string) $input);
}
private function isValid(string $input): bool
{
$sum = 0;
$digits = array_map('intval', str_split($input));
$digits = array_map('intval', str_split((string) $input));
$numDigits = count($digits);
$parity = $numDigits % 2;
for ($i = 0; $i < $numDigits; ++$i) {

View file

@ -21,7 +21,7 @@ use function preg_match;
)]
final class MacAddress extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -20,7 +20,7 @@ use function is_numeric;
)]
final class Negative extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_numeric($input)) {
return false;

View file

@ -26,7 +26,7 @@ use function str_split;
)]
final class NfeAccessKey extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (mb_strlen($input) !== 44) {
return false;

View file

@ -29,7 +29,7 @@ use function str_split;
)]
final class Nif extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -26,7 +26,7 @@ use function str_split;
)]
final class Nip extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;

View file

@ -22,7 +22,7 @@ use function preg_match;
)]
final class NoWhitespace extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (is_null($input)) {
return true;

View file

@ -11,8 +11,8 @@ namespace Respect\Validation\Rules;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Rule;
use Respect\Validation\Rules\Core\Composite;
use Respect\Validation\Validatable;
use function array_map;
use function array_reduce;
@ -25,7 +25,10 @@ final class NoneOf extends Composite
{
public function evaluate(mixed $input): Result
{
$children = array_map(static fn (Rule $rule) => $rule->evaluate($input)->withInvertedMode(), $this->rules);
$children = array_map(
static fn (Validatable $rule) => $rule->evaluate($input)->withInvertedMode(),
$this->rules
);
$valid = array_reduce($children, static fn (bool $carry, Result $result) => $carry && $result->isValid, true);
return (new Result($valid, $input, $this))->withChildren(...$children);

View file

@ -194,7 +194,7 @@ final class NotEmoji extends Simple
'\x{3299}',
];
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -20,7 +20,7 @@ use function is_null;
)]
final class NullType extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_null($input);
}

View file

@ -21,7 +21,7 @@ use function is_numeric;
)]
final class Number extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_numeric($input)) {
return false;

View file

@ -20,7 +20,7 @@ use function is_numeric;
)]
final class NumericVal extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_numeric($input);
}

View file

@ -20,7 +20,7 @@ use function is_object;
)]
final class ObjectType extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_object($input);
}

View file

@ -23,7 +23,7 @@ use const FILTER_VALIDATE_INT;
)]
final class Odd extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_numeric($input)) {
return false;

View file

@ -11,8 +11,8 @@ namespace Respect\Validation\Rules;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Rule;
use Respect\Validation\Rules\Core\Composite;
use Respect\Validation\Validatable;
use function array_map;
use function array_reduce;
@ -25,7 +25,7 @@ final class OneOf extends Composite
{
public function evaluate(mixed $input): Result
{
$children = array_map(static fn (Rule $rule) => $rule->evaluate($input), $this->rules);
$children = array_map(static fn (Validatable $rule) => $rule->evaluate($input), $this->rules);
$valid = array_reduce($children, static fn (bool $carry, Result $result) => $carry xor $result->isValid, false);
return (new Result($valid, $input, $this))->withChildren(...$children);

View file

@ -22,7 +22,7 @@ use function sqrt;
)]
final class PerfectSquare extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_numeric($input) && floor(sqrt((float) $input)) == sqrt((float) $input);
}

View file

@ -21,7 +21,7 @@ use function preg_match;
)]
final class Pesel extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;

View file

@ -21,7 +21,7 @@ use function preg_match;
)]
final class PhpLabel extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_string($input) && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $input);
}

View file

@ -23,7 +23,7 @@ use function preg_replace;
)]
final class Pis extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;

View file

@ -30,7 +30,7 @@ final class PolishIdCard extends Simple
private const ASCII_CODE_9 = 57;
private const ASCII_CODE_A = 65;
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;

View file

@ -31,7 +31,7 @@ use function strlen;
)]
final class PortugueseNif extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
// Validate format and length
if (!is_string($input)) {

View file

@ -20,7 +20,7 @@ use function is_numeric;
)]
final class Positive extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_numeric($input)) {
return false;

View file

@ -22,7 +22,7 @@ use function sqrt;
)]
final class PrimeNumber extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_numeric($input) || $input <= 1) {
return false;

View file

@ -28,7 +28,7 @@ final class PublicDomainSuffix extends Simple
{
use CanValidateUndefined;
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;

View file

@ -23,7 +23,7 @@ use function is_string;
)]
final class Readable extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if ($input instanceof SplFileInfo) {
return $input->isReadable();

View file

@ -20,7 +20,7 @@ use function is_resource;
)]
final class ResourceType extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_resource($input);
}

View file

@ -20,7 +20,7 @@ use function is_scalar;
)]
final class ScalarVal extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_scalar($input);
}

View file

@ -22,7 +22,7 @@ use function preg_match;
)]
final class Slug extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input) || mb_strstr($input, '--')) {
return false;

View file

@ -20,7 +20,7 @@ use function is_string;
)]
final class StringType extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_string($input);
}

View file

@ -22,7 +22,7 @@ use function method_exists;
)]
final class StringVal extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return is_scalar($input) || (is_object($input) && method_exists($input, '__toString'));
}

View file

@ -22,7 +22,7 @@ use function is_string;
)]
final class SymbolicLink extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if ($input instanceof SplFileInfo) {
return $input->isLink();

View file

@ -239,7 +239,7 @@ final class Tld extends Simple
'ZERO', 'ZIP', 'ZM', 'ZONE', 'ZUERICH', 'ZW',
];
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_scalar($input)) {
return false;

View file

@ -23,7 +23,7 @@ use const FILTER_VALIDATE_BOOLEAN;
)]
final class TrueVal extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === true;
}

View file

@ -23,7 +23,7 @@ use const SORT_REGULAR;
)]
final class Unique extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_array($input)) {
return false;

View file

@ -23,10 +23,10 @@ use function is_uploaded_file;
)]
final class Uploaded extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if ($input instanceof SplFileInfo) {
return $this->validate($input->getPathname());
return $this->isValid($input->getPathname());
}
if ($input instanceof UploadedFileInterface) {

View file

@ -21,7 +21,7 @@ use function mb_strtoupper;
)]
final class Uppercase extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -24,7 +24,7 @@ use function preg_match;
)]
final class Version extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -23,7 +23,7 @@ use function is_writable;
)]
final class Writable extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if ($input instanceof SplFileInfo) {
return $input->isWritable();

View file

@ -29,7 +29,7 @@ final class Yes extends Simple
) {
}
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;

View file

@ -9,25 +9,17 @@ declare(strict_types=1);
namespace Respect\Validation;
use Respect\Validation\Exceptions\ValidationException;
interface Validatable extends Rule
interface Validatable
{
public function assert(mixed $input): void;
public const TEMPLATE_STANDARD = '__standard__';
public function check(mixed $input): void;
public function evaluate(mixed $input): Result;
/**
* @param mixed[] $extraParameters
*/
public function reportError(mixed $input, array $extraParameters = []): ValidationException;
public function getName(): ?string;
public function setName(string $name): static;
public function getTemplate(): ?string;
/**
* @return array<string, mixed>
*/
public function getParams(): array;
public function validate(mixed $input): bool;
public function setTemplate(string $template): static;
}

View file

@ -9,14 +9,14 @@ declare(strict_types=1);
namespace Respect\Validation;
use Respect\Validation\Exceptions\ValidatorException;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Helpers\CanBindEvaluateRule;
use Respect\Validation\Message\Formatter;
use Respect\Validation\Message\StandardFormatter;
use Respect\Validation\Message\StandardRenderer;
use Respect\Validation\Mixins\StaticValidator;
use Respect\Validation\Rules\AbstractRule;
use Respect\Validation\Rules\AllOf;
use Respect\Validation\Rules\Core\Standard;
use function count;
use function current;
@ -24,7 +24,7 @@ use function current;
/**
* @mixin StaticValidator
*/
final class Validator extends AbstractRule
final class Validator extends Standard
{
use CanBindEvaluateRule;
@ -61,7 +61,7 @@ final class Validator extends AbstractRule
return $this->bindEvaluate($this->rule(), $this, $input);
}
public function validate(mixed $input): bool
public function isValid(mixed $input): bool
{
return $this->evaluate($input)->isValid;
}
@ -74,11 +74,11 @@ final class Validator extends AbstractRule
}
$templates = $this->templates;
if (count($templates) === 0 && $this->template != null) {
$templates = ['__self__' => $this->template];
if (count($templates) === 0 && $this->getTemplate() != null) {
$templates = ['__self__' => $this->getTemplate()];
}
throw new ValidatorException(
throw new ValidationException(
$this->formatter->main($result, $templates),
$this->formatter->full($result, $templates),
$this->formatter->array($result, $templates),
@ -99,6 +99,22 @@ final class Validator extends AbstractRule
return $this->rules;
}
/**
* @deprecated Use {@see isValid()} instead.
*/
public function validate(mixed $input): bool
{
return $this->evaluate($input)->isValid;
}
/**
* @deprecated Use {@see assert()} instead.
*/
public function check(mixed $input): void
{
$this->assert($input);
}
private function rule(): Validatable
{
if (count($this->rules) === 1) {

View file

@ -7,7 +7,7 @@
declare(strict_types=1);
use Respect\Validation\Exceptions\ValidatorException;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validatable;
use Symfony\Component\VarExporter\VarExporter;
@ -18,7 +18,7 @@ function exceptionMessage(callable $callable, string $fallbackMessage = 'No exce
try {
$callable();
echo $fallbackMessage . PHP_EOL;
} catch (ValidatorException $exception) {
} catch (ValidationException $exception) {
echo $exception->getMessage() . PHP_EOL;
}
}
@ -28,7 +28,7 @@ function exceptionMessages(callable $callable, string $fallbackMessage = 'No exc
try {
$callable();
echo $fallbackMessage . PHP_EOL;
} catch (ValidatorException $exception) {
} catch (ValidationException $exception) {
echo VarExporter::export($exception->getMessages()) . PHP_EOL;
}
}
@ -38,7 +38,7 @@ function exceptionFullMessage(callable $callable, string $fallbackMessage = 'No
try {
$callable();
echo $fallbackMessage . PHP_EOL;
} catch (ValidatorException $exception) {
} catch (ValidationException $exception) {
echo $exception->getFullMessage() . PHP_EOL;
}
}

View file

@ -11,7 +11,6 @@ namespace Respect\Validation\Test\Builders;
use Respect\Validation\Mode;
use Respect\Validation\Result;
use Respect\Validation\Rule;
use Respect\Validation\Test\Rules\Stub;
use Respect\Validation\Validatable;
@ -23,7 +22,7 @@ final class ResultBuilder
private Mode $mode = Mode::DEFAULT;
private string $template = Rule::TEMPLATE_STANDARD;
private string $template = Validatable::TEMPLATE_STANDARD;
/** @var array<string, mixed> */
private array $parameters = [];

View file

@ -1,16 +0,0 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Test\Exceptions;
use Respect\Validation\Exceptions\ValidationException;
final class StubException extends ValidationException
{
}

View file

@ -13,7 +13,7 @@ use Respect\Validation\Rules\Core\Simple;
final class ConcreteSimple extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return true;
}

View file

@ -9,11 +9,11 @@ declare(strict_types=1);
namespace Respect\Validation\Test\Rules;
use Respect\Validation\Rules\AbstractRule;
use Respect\Validation\Rules\Core\Simple;
final class CustomRule extends AbstractRule
final class CustomRule extends Simple
{
public function validate(mixed $input): bool
protected function isValid(mixed $input): bool
{
return false;
}

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