diff --git a/docs/08-list-of-rules-by-category.md b/docs/08-list-of-rules-by-category.md index a1c2736d5..d78766dd4 100644 --- a/docs/08-list-of-rules-by-category.md +++ b/docs/08-list-of-rules-by-category.md @@ -51,6 +51,7 @@ - [GreaterThanOrEqual](rules/GreaterThanOrEqual.md) - [Identical](rules/Identical.md) - [In](rules/In.md) +- [Length](rules/Length.md) - [LessThan](rules/LessThan.md) - [LessThanOrEqual](rules/LessThanOrEqual.md) - [Max](rules/Max.md) @@ -260,6 +261,7 @@ - [Call](rules/Call.md) - [Each](rules/Each.md) +- [Length](rules/Length.md) - [Max](rules/Max.md) - [Min](rules/Min.md) diff --git a/docs/rules/GreaterThan.md b/docs/rules/GreaterThan.md index 55fe558bd..901156b1c 100644 --- a/docs/rules/GreaterThan.md +++ b/docs/rules/GreaterThan.md @@ -30,6 +30,7 @@ See also: - [Between](Between.md) - [BetweenExclusive](BetweenExclusive.md) - [GreaterThanOrEqual](GreaterThanOrEqual.md) +- [Length](Length.md) - [LessThanOrEqual](LessThanOrEqual.md) - [Max](Max.md) - [Min](Min.md) diff --git a/docs/rules/Length.md b/docs/rules/Length.md index 587d7d722..039dd65e9 100644 --- a/docs/rules/Length.md +++ b/docs/rules/Length.md @@ -1,54 +1,45 @@ # Length -- `Length(int $min, int $max)` -- `Length(int $min, null)` -- `Length(null, int $max)` -- `Length(int $min, int $max, bool $inclusive)` +- `Length(Validatable $rule)` -Validates the length of the given input. - -Most simple example: +Validates the length of the given input against a given rule. ```php -v::stringType()->length(1, 5)->validate('abc'); // true -``` +v::length(v::between(1, 5))->validate('abc'); // true -You can also validate only minimum length: +v::length(v::greaterThan(5))->validate('abcdef'); // true -```php -v::stringType()->length(5, null)->validate('abcdef'); // true +v::length(v::lessThan(5))->validate('abc'); // true ``` -Only maximum length: +This rule can be used to validate the length of strings, arrays, and objects that implement the `Countable` interface. ```php -v::stringType()->length(null, 5)->validate('abc'); // true -``` - -The type as the first validator in a chain is a good practice, -since length accepts many types: +v::length(v::greaterThanOrEqual(3))->validate([1, 2, 3]); // true -```php -v::arrayVal()->length(1, 5)->validate(['foo', 'bar']); // true +v::length(v::equals(0))->validate(new SplPriorityQueue()); // true ``` -A third parameter may be passed to validate the passed values inclusive: - -```php -v::stringType()->length(1, 5, true)->validate('a'); // true -``` +## Categorization -Message template for this validator includes `{{minValue}}` and `{{maxValue}}`. +- Comparisons +- Transformations ## Changelog -Version | Description ---------|------------- - 0.3.9 | Created +| Version | Description | +|--------:|-------------------------| +| 3.0.0 | Became a transformation | +| 0.3.9 | Created | *** See also: - [Between](Between.md) - [BetweenExclusive](BetweenExclusive.md) +- [GreaterThan](GreaterThan.md) - [GreaterThanOrEqual](GreaterThanOrEqual.md) +- [LessThan](LessThan.md) +- [LessThanOrEqual](LessThanOrEqual.md) +- [Max](Max.md) +- [Min](Min.md) diff --git a/docs/rules/LessThan.md b/docs/rules/LessThan.md index bb7e9a93e..dfe3108d9 100644 --- a/docs/rules/LessThan.md +++ b/docs/rules/LessThan.md @@ -30,6 +30,7 @@ See also: - [Between](Between.md) - [BetweenExclusive](BetweenExclusive.md) - [GreaterThanOrEqual](GreaterThanOrEqual.md) +- [Length](Length.md) - [LessThanOrEqual](LessThanOrEqual.md) - [Max](Max.md) - [Min](Min.md) diff --git a/docs/rules/LessThanOrEqual.md b/docs/rules/LessThanOrEqual.md index be14d2b0b..a058bc967 100644 --- a/docs/rules/LessThanOrEqual.md +++ b/docs/rules/LessThanOrEqual.md @@ -35,6 +35,7 @@ See also: - [BetweenExclusive](BetweenExclusive.md) - [GreaterThan](GreaterThan.md) - [GreaterThanOrEqual](GreaterThanOrEqual.md) +- [Length](Length.md) - [LessThan](LessThan.md) - [Max](Max.md) - [MaxAge](MaxAge.md) diff --git a/docs/rules/Max.md b/docs/rules/Max.md index 9a3a2203c..e1210dd9d 100644 --- a/docs/rules/Max.md +++ b/docs/rules/Max.md @@ -42,6 +42,7 @@ See also: - [GreaterThan](GreaterThan.md) - [GreaterThanOrEqual](GreaterThanOrEqual.md) - [IterableType](IterableType.md) +- [Length](Length.md) - [LessThan](LessThan.md) - [LessThanOrEqual](LessThanOrEqual.md) - [Min](Min.md) diff --git a/docs/rules/Min.md b/docs/rules/Min.md index ff7221c65..80fce44c8 100644 --- a/docs/rules/Min.md +++ b/docs/rules/Min.md @@ -42,6 +42,7 @@ See also: - [Each](Each.md) - [GreaterThan](GreaterThan.md) - [GreaterThanOrEqual](GreaterThanOrEqual.md) +- [Length](Length.md) - [LessThan](LessThan.md) - [LessThanOrEqual](LessThanOrEqual.md) - [Max](Max.md) diff --git a/library/Factory.php b/library/Factory.php index 59871857f..b69660da3 100644 --- a/library/Factory.php +++ b/library/Factory.php @@ -25,6 +25,7 @@ use Respect\Validation\Transformers\DeprecatedKey; use Respect\Validation\Transformers\DeprecatedKeyNested; use Respect\Validation\Transformers\DeprecatedKeyValue; +use Respect\Validation\Transformers\DeprecatedLength; use Respect\Validation\Transformers\DeprecatedMinAndMax; use Respect\Validation\Transformers\DeprecatedType; use Respect\Validation\Transformers\RuleSpec; @@ -64,7 +65,7 @@ public function __construct() new DeprecatedKey( new DeprecatedKeyValue( new DeprecatedMinAndMax( - new DeprecatedKeyNested(new DeprecatedType()) + new DeprecatedKeyNested(new DeprecatedLength(new DeprecatedType())) ) ) ) diff --git a/library/Mixins/ChainedValidator.php b/library/Mixins/ChainedValidator.php index 801333902..ef8ac36e7 100644 --- a/library/Mixins/ChainedValidator.php +++ b/library/Mixins/ChainedValidator.php @@ -188,7 +188,7 @@ public function leapDate(string $format): ChainedValidator; public function leapYear(): ChainedValidator; - public function length(?int $min = null, ?int $max = null, bool $inclusive = true): ChainedValidator; + public function length(Validatable $rule): ChainedValidator; public function lowercase(): ChainedValidator; diff --git a/library/Mixins/StaticValidator.php b/library/Mixins/StaticValidator.php index 9170f2538..c09530a7e 100644 --- a/library/Mixins/StaticValidator.php +++ b/library/Mixins/StaticValidator.php @@ -190,7 +190,7 @@ public static function leapDate(string $format): ChainedValidator; public static function leapYear(): ChainedValidator; - public static function length(?int $min = null, ?int $max = null, bool $inclusive = true): ChainedValidator; + public static function length(Validatable $rule): ChainedValidator; public static function lowercase(): ChainedValidator; diff --git a/library/Rules/Domain.php b/library/Rules/Domain.php index 819d3eec6..30e12acff 100644 --- a/library/Rules/Domain.php +++ b/library/Rules/Domain.php @@ -58,7 +58,12 @@ public function evaluate(mixed $input): Result private function createGenericRule(): Consecutive { - return new Consecutive(new StringType(), new NoWhitespace(), new Contains('.'), new Length(3)); + return new Consecutive( + new StringType(), + new NoWhitespace(), + new Contains('.'), + new Length(new GreaterThanOrEqual(3)) + ); } private function createTldRule(bool $realTldCheck): Validatable @@ -67,7 +72,7 @@ private function createTldRule(bool $realTldCheck): Validatable return new Tld(); } - return new Consecutive(new Not(new StartsWith('-')), new Length(2)); + return new Consecutive(new Not(new StartsWith('-')), new Length(new GreaterThanOrEqual(2))); } private function createPartsRule(): Validatable diff --git a/library/Rules/Length.php b/library/Rules/Length.php index 67ee1f2c4..4ad9d0dcf 100644 --- a/library/Rules/Length.php +++ b/library/Rules/Length.php @@ -9,151 +9,43 @@ namespace Respect\Validation\Rules; -use Countable as CountableInterface; -use Respect\Validation\Exceptions\ComponentException; +use Countable as PhpCountable; +use Respect\Validation\Helpers\CanBindEvaluateRule; use Respect\Validation\Message\Template; +use Respect\Validation\Result; +use Respect\Validation\Rules\Core\Wrapper; 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; +use function Respect\Stringifier\stringify; -#[Template( - '{{name}} must have a length between {{minValue}} and {{maxValue}}', - '{{name}} must not have a length between {{minValue}} and {{maxValue}}', - self::TEMPLATE_BOTH, -)] -#[Template( - '{{name}} must have a length greater than {{minValue}}', - '{{name}} must not have a length greater than {{minValue}}', - self::TEMPLATE_LOWER, -)] -#[Template( - '{{name}} must have a length greater than or equal to {{minValue}}', - '{{name}} must not have a length greater than or equal to {{minValue}}', - self::TEMPLATE_LOWER_INCLUSIVE, -)] -#[Template( - '{{name}} must have a length lower than {{maxValue}}', - '{{name}} must not have a length lower than {{maxValue}}', - self::TEMPLATE_GREATER, -)] -#[Template( - '{{name}} must have a length lower than or equal to {{maxValue}}', - '{{name}} must not have a length lower than or equal to {{maxValue}}', - self::TEMPLATE_GREATER_INCLUSIVE, -)] -#[Template( - '{{name}} must have a length of {{maxValue}}', - '{{name}} must not have a length of {{maxValue}}', - self::TEMPLATE_EXACT, -)] -final class Length extends AbstractRule +#[Template('The length of', 'The length of')] +final class Length extends Wrapper { - public const TEMPLATE_LOWER = '__lower__'; - public const TEMPLATE_GREATER = '__greater__'; - public const TEMPLATE_GREATER_INCLUSIVE = '__greater_inclusive__'; - public const TEMPLATE_EXACT = '__exact__'; - public const TEMPLATE_LOWER_INCLUSIVE = '__lower_inclusive__'; - public const TEMPLATE_BOTH = '__both__'; + use CanBindEvaluateRule; - public function __construct( - private readonly ?int $minValue = null, - private readonly ?int $maxValue = null, - private readonly bool $inclusive = true - ) { - - if ($maxValue !== null && $minValue > $maxValue) { - throw new ComponentException(sprintf('%d cannot be less than %d for validation', $minValue, $maxValue)); - } - } - - public function validate(mixed $input): bool + public function evaluate(mixed $input): Result { - $length = $this->extractLength($input); - if ($length === null) { - return false; - } + $typeResult = $this->bindEvaluate(new OneOf(new StringType(), new Countable()), $this, $input); + if (!$typeResult->isValid) { + $result = $this->rule->evaluate(null)->withNameIfMissing(stringify($input)); - return $this->validateMin($length) && $this->validateMax($length); - } - - /** - * @return array - */ - public function getParams(): array - { - return [ - 'minValue' => $this->minValue, - 'maxValue' => $this->maxValue, - ]; - } - - protected function getStandardTemplate(mixed $input): string - { - if (!$this->minValue) { - return $this->inclusive === true ? self::TEMPLATE_GREATER_INCLUSIVE : self::TEMPLATE_GREATER; - } - - if (!$this->maxValue) { - return $this->inclusive === true ? self::TEMPLATE_LOWER_INCLUSIVE : self::TEMPLATE_LOWER; + return Result::failed($input, $this)->withNextSibling($result); } - if ($this->minValue == $this->maxValue) { - return self::TEMPLATE_EXACT; - } + $result = $this->rule->evaluate($this->extractLength($input))->withNameIfMissing(stringify($input)); - return self::TEMPLATE_BOTH; + return (new Result($result->isValid, $input, $this))->withNextSibling($result); } - private function extractLength(mixed $input): ?int + /** @param array|PhpCountable|string $input */ + private function extractLength(array|PhpCountable|string $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; + return count($input); } } diff --git a/library/Transformers/DeprecatedLength.php b/library/Transformers/DeprecatedLength.php new file mode 100644 index 000000000..c081bc146 --- /dev/null +++ b/library/Transformers/DeprecatedLength.php @@ -0,0 +1,107 @@ + + * SPDX-License-Identifier: MIT + */ + +declare(strict_types=1); + +namespace Respect\Validation\Transformers; + +use Respect\Validation\Rules\Between; +use Respect\Validation\Rules\BetweenExclusive; +use Respect\Validation\Rules\Equals; +use Respect\Validation\Rules\GreaterThan; +use Respect\Validation\Rules\GreaterThanOrEqual; +use Respect\Validation\Rules\LessThan; +use Respect\Validation\Rules\LessThanOrEqual; +use Respect\Validation\Validatable; + +use function sprintf; +use function trigger_error; +use function var_export; + +use const E_USER_DEPRECATED; + +final class DeprecatedLength implements Transformer +{ + public function __construct( + private readonly Transformer $next + ) { + } + + public function transform(RuleSpec $ruleSpec): RuleSpec + { + if ($ruleSpec->name !== 'length' || $ruleSpec->arguments === []) { + return $this->next->transform($ruleSpec); + } + + if (isset($ruleSpec->arguments[0]) && $ruleSpec->arguments[0] instanceof Validatable) { + return $this->next->transform($ruleSpec); + } + + $minValue = $ruleSpec->arguments[0] ?? null; + $maxValue = $ruleSpec->arguments[1] ?? null; + $inclusive = $ruleSpec->arguments[2] ?? true; + + $message = 'Calling length() with scalar values has been deprecated, ' . + 'and will not be allowed in the next major version. '; + + if (!$minValue) { + trigger_error( + sprintf( + $message . 'Use length(%s(%s)) instead.', + $inclusive ? 'lessThanOrEqual' : 'lessThan', + var_export($maxValue, true), + ), + E_USER_DEPRECATED + ); + + return new RuleSpec( + 'length', + [$inclusive ? new LessThanOrEqual($maxValue) : new LessThan($maxValue)] + ); + } + + if (!$maxValue) { + trigger_error( + sprintf( + $message . 'Use length(%s(%s)) instead.', + $inclusive ? 'greaterThanOrEqual' : 'greaterThan', + var_export($minValue, true), + ), + E_USER_DEPRECATED + ); + + return new RuleSpec( + 'length', + [$inclusive ? new GreaterThanOrEqual($minValue) : new GreaterThan($minValue)] + ); + } + + if ($minValue === $maxValue) { + trigger_error( + sprintf($message . 'Use length(equals(%s)) instead.', var_export($minValue, true)), + E_USER_DEPRECATED + ); + + return new RuleSpec('length', [new Equals($minValue)]); + } + + trigger_error( + sprintf( + $message . 'Use length(%s(%s, %s)) instead.', + $inclusive ? 'between' : 'betweenExclusive', + var_export($minValue, true), + var_export($maxValue, true), + ), + E_USER_DEPRECATED + ); + + return new RuleSpec( + 'length', + [$inclusive ? new Between($minValue, $maxValue) : new BetweenExclusive($minValue, $maxValue)] + ); + } +} diff --git a/tests/integration/do_not_rely_on_nested_validation_exception_interface_for_check.phpt b/tests/integration/do_not_rely_on_nested_validation_exception_interface_for_check.phpt index 3b8ff3720..bc81e1360 100644 --- a/tests/integration/do_not_rely_on_nested_validation_exception_interface_for_check.phpt +++ b/tests/integration/do_not_rely_on_nested_validation_exception_interface_for_check.phpt @@ -10,7 +10,10 @@ require 'vendor/autoload.php'; use Respect\Validation\Validator; exceptionMessage( - static fn () => Validator::alnum('__')->length(1, 15)->noWhitespace()->check('really messed up screen#name') + static fn () => Validator::alnum('__') + ->length(Validator::between(1, 15)) + ->noWhitespace() + ->check('really messed up screen#name') ); ?> --EXPECT-- diff --git a/tests/integration/get_full_message_should_include_all_validation_messages_in_a_chain.phpt b/tests/integration/get_full_message_should_include_all_validation_messages_in_a_chain.phpt index 41d1289ec..ae62bf731 100644 --- a/tests/integration/get_full_message_should_include_all_validation_messages_in_a_chain.phpt +++ b/tests/integration/get_full_message_should_include_all_validation_messages_in_a_chain.phpt @@ -1,5 +1,3 @@ ---TEST-- -getFullMessage() should include all validation messages in a chain --FILE-- Validator::stringType()->length(2, 15)->assert(0)); +exceptionFullMessage( + static fn() => Validator::stringType()->length(Validator::between(2, 15))->assert(0) +); ?> --EXPECT-- - All of the required rules must pass for 0 - 0 must be of type string - - 0 must have a length between 2 and 15 + - The length of 0 must be between 2 and 15 diff --git a/tests/integration/get_messages_should_include_all_validation_messages_in_a_chain.phpt b/tests/integration/get_messages_should_include_all_validation_messages_in_a_chain.phpt index a6eb6d180..d0401a332 100644 --- a/tests/integration/get_messages_should_include_all_validation_messages_in_a_chain.phpt +++ b/tests/integration/get_messages_should_include_all_validation_messages_in_a_chain.phpt @@ -1,5 +1,3 @@ ---TEST-- -getMessages() should include all validation messages in a chain --FILE-- key('username', Validator::length(2, 32)) + ->key('username', Validator::length(Validator::between(2, 32))) ->key('birthdate', Validator::dateTime()) ->key('password', Validator::notEmpty()) ->key('email', Validator::email()) @@ -28,7 +26,7 @@ exceptionMessages(static function (): void { ?> --EXPECT-- [ - 'username' => 'username must have a length between 2 and 32', + 'username' => 'The length of username must be between 2 and 32', 'birthdate' => 'birthdate must be a valid date/time', 'password' => 'password must not be empty', 'email' => 'email must be present', diff --git a/tests/integration/issue-1376.phpt b/tests/integration/issue-1376.phpt index 015a8b902..11b030804 100644 --- a/tests/integration/issue-1376.phpt +++ b/tests/integration/issue-1376.phpt @@ -8,10 +8,10 @@ require 'vendor/autoload.php'; use Respect\Validation\Validator as v; exceptionFullMessage(static function (): void { - v::property('title', v::length(2, 3)->stringType()) + v::property('title', v::length(v::between(2, 3))->stringType()) ->property('description', v::stringType()) - ->property('author', v::intType()->length(1, 2)) - ->property('user', v::intVal()->length(1, 2)) + ->property('author', v::intType()->length(v::between(1, 2))) + ->property('user', v::intVal()->length(v::between(1, 2))) ->assert((object) ['author' => 'foo']); }); @@ -22,5 +22,5 @@ exceptionFullMessage(static function (): void { - description must be present - All of the required rules must pass for author - author must be of type integer - - author must have a length between 1 and 2 + - The length of author must be between 1 and 2 - user must be present diff --git a/tests/integration/issue-425.phpt b/tests/integration/issue-425.phpt index eb08addb7..c5e813a1d 100644 --- a/tests/integration/issue-425.phpt +++ b/tests/integration/issue-425.phpt @@ -9,7 +9,7 @@ use Respect\Validation\Validator as v; $validator = v::create() ->key('age', v::intType()->notEmpty()->noneOf(v::stringType(), v::arrayType())) - ->key('reference', v::stringType()->notEmpty()->length(1, 50)); + ->key('reference', v::stringType()->notEmpty()->length(v::between(1, 50))); exceptionFullMessage(static fn() => $validator->assert(['age' => 1])); exceptionFullMessage(static fn() => $validator->assert(['reference' => 'QSF1234'])); diff --git a/tests/integration/issue-446.phpt b/tests/integration/issue-446.phpt index 7ac25fab4..d891aab1c 100644 --- a/tests/integration/issue-446.phpt +++ b/tests/integration/issue-446.phpt @@ -13,13 +13,10 @@ $arr = [ ]; exceptionMessages(static function () use ($arr): void { - v::create() - ->key('name', v::length(2, 32)) - ->key('email', v::email()) - ->assert($arr); + v::create()->key('name', v::length(v::between(2, 32)))->key('email', v::email())->assert($arr); }); ?> --EXPECT-- [ - 'name' => 'name must have a length between 2 and 32', + 'name' => 'The length of name must be between 2 and 32', ] diff --git a/tests/integration/keys_as_validator_names.phpt b/tests/integration/keys_as_validator_names.phpt index 1a24a0dc2..ac8f1861c 100644 --- a/tests/integration/keys_as_validator_names.phpt +++ b/tests/integration/keys_as_validator_names.phpt @@ -1,5 +1,3 @@ ---TEST-- -keys as validator names --FILE-- key('username', Validator::length(2, 32)) + ->key('username', Validator::length(Validator::between(2, 32))) ->key('birthdate', Validator::dateTime()) ->setName('User Subscription Form') ->assert(['username' => '0', 'birthdate' => 'Whatever']); @@ -21,5 +19,5 @@ exceptionFullMessage(static function (): void { ?> --EXPECT-- - All of the required rules must pass for User Subscription Form - - username must have a length between 2 and 32 + - The length of username must be between 2 and 32 - birthdate must be a valid date/time diff --git a/tests/integration/readme/custom_messages.phpt b/tests/integration/readme/custom_messages.phpt index 844b1022f..b7ace79d5 100644 --- a/tests/integration/readme/custom_messages.phpt +++ b/tests/integration/readme/custom_messages.phpt @@ -10,7 +10,7 @@ use Respect\Validation\Validator as v; exceptionMessages( static fn() => v::alnum() ->noWhitespace() - ->length(1, 15) + ->length(v::between(1, 15)) ->setTemplates([ 'alnum' => '{{name}} must contain only letters and digits', 'noWhitespace' => '{{name}} cannot contain spaces', diff --git a/tests/integration/readme/example_1.phpt b/tests/integration/readme/example_1.phpt index feb047c92..d138ea1ca 100644 --- a/tests/integration/readme/example_1.phpt +++ b/tests/integration/readme/example_1.phpt @@ -13,7 +13,7 @@ $user = new stdClass(); $user->name = 'Alexandre'; $user->birthdate = '1987-07-01'; -$userValidator = v::property('name', v::stringType()->length(1, 32)) +$userValidator = v::property('name', v::stringType()->length(v::between(1, 32))) ->property('birthdate', v::dateTime()->minAge(18)); $userValidator->assert($user); diff --git a/tests/integration/readme/example_2.phpt b/tests/integration/readme/example_2.phpt index 317c7c936..703bb4a4e 100644 --- a/tests/integration/readme/example_2.phpt +++ b/tests/integration/readme/example_2.phpt @@ -7,10 +7,12 @@ require 'vendor/autoload.php'; use Respect\Validation\Validator as v; -exceptionFullMessage(static fn() => v::alnum()->noWhitespace()->length(1, 15)->assert('really messed up screen#name')); +exceptionFullMessage( + static fn() => v::alnum()->noWhitespace()->length(v::between(1, 15))->assert('really messed up screen#name') +); ?> --EXPECT-- - All of the required rules must pass for "really messed up screen#name" - "really messed up screen#name" must contain only letters (a-z) and digits (0-9) - "really messed up screen#name" must not contain whitespace - - "really messed up screen#name" must have a length between 1 and 15 + - The length of "really messed up screen#name" must be between 1 and 15 diff --git a/tests/integration/readme/example_4.phpt b/tests/integration/readme/example_4.phpt deleted file mode 100644 index 752c0e57a..000000000 --- a/tests/integration/readme/example_4.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---FILE-- - v::alnum()->noWhitespace()->length(1, 15)->assert('really messed up screen#name')); -?> ---EXPECT-- -[ - 'alnum' => '"really messed up screen#name" must contain only letters (a-z) and digits (0-9)', - 'noWhitespace' => '"really messed up screen#name" must not contain whitespace', - 'length' => '"really messed up screen#name" must have a length between 1 and 15', -] diff --git a/tests/integration/readme/getting_messages_as_an_array.phpt b/tests/integration/readme/getting_messages_as_an_array.phpt index 3bba57330..706a4898d 100644 --- a/tests/integration/readme/getting_messages_as_an_array.phpt +++ b/tests/integration/readme/getting_messages_as_an_array.phpt @@ -7,11 +7,13 @@ require 'vendor/autoload.php'; use Respect\Validation\Validator as v; -exceptionMessages(static fn() => v::alnum()->noWhitespace()->length(1, 15)->assert('really messed up screen#name')); +exceptionMessages( + static fn() => v::alnum()->noWhitespace()->length(v::between(1, 15))->assert('really messed up screen#name') +); ?> --EXPECT-- [ 'alnum' => '"really messed up screen#name" must contain only letters (a-z) and digits (0-9)', 'noWhitespace' => '"really messed up screen#name" must not contain whitespace', - 'length' => '"really messed up screen#name" must have a length between 1 and 15', + 'length' => 'The length of "really messed up screen#name" must be between 1 and 15', ] diff --git a/tests/integration/rules/deprecated/length.phpt b/tests/integration/rules/deprecated/length.phpt new file mode 100644 index 000000000..ba44b543d --- /dev/null +++ b/tests/integration/rules/deprecated/length.phpt @@ -0,0 +1,108 @@ +--FILE-- + v::length(0, 5, false)->check('forest')); +exceptionMessage(static fn() => v::length(10, 20)->check('river')); +exceptionMessage(static fn() => v::length(15, null, false)->check('mountain')); +exceptionMessage(static fn() => v::length(20)->check('ocean')); +exceptionMessage(static fn() => v::length(2, 5)->check('desert')); +exceptionMessage(static fn() => v::not(v::length(0, 15))->check('rainforest')); +exceptionMessage(static fn() => v::not(v::length(0, 20, false))->check('glacier')); +exceptionMessage(static fn() => v::not(v::length(3, null))->check('meadow')); +exceptionMessage(static fn() => v::not(v::length(5, null, false))->check('volcano')); +exceptionMessage(static fn() => v::not(v::length(5, 20))->check('canyon')); +exceptionFullMessage(static fn() => v::length(0, 5, false)->assert('prairie')); +exceptionFullMessage(static fn() => v::length(0, 5)->assert('wetland')); +exceptionFullMessage(static fn() => v::length(15, null, false)->assert('tundra')); +exceptionFullMessage(static fn() => v::length(20)->assert('savanna')); +exceptionFullMessage(static fn() => v::length(7, 10)->assert('marsh')); +exceptionFullMessage(static fn() => v::length(4, 10, false)->assert('reef')); +exceptionFullMessage(static fn() => v::not(v::length(0, 15))->assert('valley')); +exceptionFullMessage(static fn() => v::not(v::length(0, 20, false))->assert('island')); +exceptionFullMessage(static fn() => v::not(v::length(5, null))->assert('plateau')); +exceptionFullMessage(static fn() => v::not(v::length(3, null, false))->assert('fjord')); +exceptionFullMessage(static fn() => v::not(v::length(5, 20))->assert('delta')); +exceptionFullMessage(static fn() => v::not(v::length(5, 11, false))->assert('waterfall')); +exceptionFullMessage(static fn() => v::length(8, 8)->assert('estuary')); +exceptionFullMessage(static fn() => v::not(v::length(5, 5))->assert('grove')); +// phpcs:disable Generic.Files.LineLength.TooLong +?> +--EXPECTF-- + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(lessThan(5)) instead. in %s +The length of "forest" must be less than 5 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(between(10, 20)) instead. in %s +The length of "river" must be between 10 and 20 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(greaterThan(15)) instead. in %s +The length of "mountain" must be greater than 15 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(greaterThanOrEqual(20)) instead. in %s +The length of "ocean" must be greater than or equal to 20 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(between(2, 5)) instead. in %s +The length of "desert" must be between 2 and 5 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(lessThanOrEqual(15)) instead. in %s +The length of "rainforest" must not be less than or equal to 15 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(lessThan(20)) instead. in %s +The length of "glacier" must not be less than 20 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(greaterThanOrEqual(3)) instead. in %s +The length of "meadow" must not be greater than or equal to 3 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(greaterThan(5)) instead. in %s +The length of "volcano" must not be greater than 5 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(between(5, 20)) instead. in %s +The length of "canyon" must not be between 5 and 20 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(lessThan(5)) instead. in %s +- The length of "prairie" must be less than 5 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(lessThanOrEqual(5)) instead. in %s +- The length of "wetland" must be less than or equal to 5 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(greaterThan(15)) instead. in %s +- The length of "tundra" must be greater than 15 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(greaterThanOrEqual(20)) instead. in %s +- The length of "savanna" must be greater than or equal to 20 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(between(7, 10)) instead. in %s +- The length of "marsh" must be between 7 and 10 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(betweenExclusive(4, 10)) instead. in %s +- The length of "reef" must be greater than 4 and less than 10 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(lessThanOrEqual(15)) instead. in %s +- The length of "valley" must not be less than or equal to 15 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(lessThan(20)) instead. in %s +- The length of "island" must not be less than 20 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(greaterThanOrEqual(5)) instead. in %s +- The length of "plateau" must not be greater than or equal to 5 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(greaterThan(3)) instead. in %s +- The length of "fjord" must not be greater than 3 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(between(5, 20)) instead. in %s +- The length of "delta" must not be between 5 and 20 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(betweenExclusive(5, 11)) instead. in %s +- The length of "waterfall" must not be greater than 5 and less than 11 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(equals(8)) instead. in %s +- The length of "estuary" must equal 8 + +Deprecated: Calling length() with scalar values has been deprecated, and will not be allowed in the next major version. Use length(equals(5)) instead. in %s +- The length of "grove" must not equal 5 diff --git a/tests/integration/rules/length.phpt b/tests/integration/rules/length.phpt index 660f0e99f..9569b6855 100644 --- a/tests/integration/rules/length.phpt +++ b/tests/integration/rules/length.phpt @@ -3,49 +3,55 @@ declare(strict_types=1); -require_once 'vendor/autoload.php'; +require 'vendor/autoload.php'; use Respect\Validation\Validator as v; -exceptionMessage(static fn() => v::length(0, 5, false)->check('phpsp.org.br')); -exceptionMessage(static fn() => v::length(0, 10)->check('phpsp.org.br')); -exceptionMessage(static fn() => v::length(15, null, false)->check('phpsp.org.br')); -exceptionMessage(static fn() => v::length(20)->check('phpsp.org.br')); -exceptionMessage(static fn() => v::length(5, 10)->check('phpsp.org.br')); -exceptionMessage(static fn() => v::not(v::length(0, 15))->check('phpsp.org.br')); -exceptionMessage(static fn() => v::not(v::length(0, 20, false))->check('phpsp.org.br')); -exceptionMessage(static fn() => v::not(v::length(10, null))->check('phpsp.org.br')); -exceptionMessage(static fn() => v::not(v::length(5, null, false))->check('phpsp.org.br')); -exceptionMessage(static fn() => v::not(v::length(5, 20))->check('phpsp.org.br')); -exceptionFullMessage(static fn() => v::length(0, 5, false)->assert('phpsp.org.br')); -exceptionFullMessage(static fn() => v::length(0, 10)->assert('phpsp.org.br')); -exceptionFullMessage(static fn() => v::length(15, null, false)->assert('phpsp.org.br')); -exceptionFullMessage(static fn() => v::length(20)->assert('phpsp.org.br')); -exceptionFullMessage(static fn() => v::length(5, 10)->assert('phpsp.org.br')); -exceptionFullMessage(static fn() => v::not(v::length(0, 15))->assert('phpsp.org.br')); -exceptionFullMessage(static fn() => v::not(v::length(0, 20, false))->assert('phpsp.org.br')); -exceptionFullMessage(static fn() => v::not(v::length(10, null))->assert('phpsp.org.br')); -exceptionFullMessage(static fn() => v::not(v::length(5, null, false))->assert('phpsp.org.br')); -exceptionFullMessage(static fn() => v::not(v::length(5, 20))->assert('phpsp.org.br')); +run([ + 'Default' => [v::length(v::equals(3)), 'tulip'], + 'Negative wrapped' => [v::length(v::not(v::equals(4))), 'rose'], + 'Negative wrapper' => [v::not(v::length(v::equals(4))), 'fern'], + 'With template' => [v::length(v::equals(3)), 'azalea', 'This is a template'], + 'With wrapper name' => [v::length(v::equals(3))->setName('Cactus'), 'peyote'], +]); ?> --EXPECT-- -"phpsp.org.br" must have a length lower than 5 -"phpsp.org.br" must have a length lower than or equal to 10 -"phpsp.org.br" must have a length greater than 15 -"phpsp.org.br" must have a length greater than or equal to 20 -"phpsp.org.br" must have a length between 5 and 10 -"phpsp.org.br" must not have a length lower than or equal to 15 -"phpsp.org.br" must not have a length lower than 20 -"phpsp.org.br" must not have a length greater than or equal to 10 -"phpsp.org.br" must not have a length greater than 5 -"phpsp.org.br" must not have a length between 5 and 20 -- "phpsp.org.br" must have a length lower than 5 -- "phpsp.org.br" must have a length lower than or equal to 10 -- "phpsp.org.br" must have a length greater than 15 -- "phpsp.org.br" must have a length greater than or equal to 20 -- "phpsp.org.br" must have a length between 5 and 10 -- "phpsp.org.br" must not have a length lower than or equal to 15 -- "phpsp.org.br" must not have a length lower than 20 -- "phpsp.org.br" must not have a length greater than or equal to 10 -- "phpsp.org.br" must not have a length greater than 5 -- "phpsp.org.br" must not have a length between 5 and 20 +Default +⎺⎺⎺⎺⎺⎺⎺ +The length of "tulip" must equal 3 +- The length of "tulip" must equal 3 +[ + 'length' => 'The length of "tulip" must equal 3', +] + +Negative wrapped +⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ +The length of "rose" must not equal 4 +- The length of "rose" must not equal 4 +[ + 'length' => 'The length of "rose" must not equal 4', +] + +Negative wrapper +⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ +The length of "fern" must not equal 4 +- The length of "fern" must not equal 4 +[ + 'length' => 'The length of "fern" must not equal 4', +] + +With template +⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ +This is a template +- This is a template +[ + 'length' => 'This is a template', +] + +With wrapper name +⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ +The length of Cactus must equal 3 +- The length of Cactus must equal 3 +[ + 'Cactus' => 'The length of Cactus must equal 3', +] diff --git a/tests/integration/translator-assert.phpt b/tests/integration/translator-assert.phpt index f2564668a..4ab84b147 100644 --- a/tests/integration/translator-assert.phpt +++ b/tests/integration/translator-assert.phpt @@ -14,9 +14,13 @@ Factory::setDefaultInstance( return [ 'All of the required rules must pass for {{name}}' => 'Todas as regras requeridas devem passar para {{name}}', + 'The length of' + => 'O comprimento de', '{{name}} must be of type string' => '{{name}} deve ser do tipo string', - '{{name}} must have a length between {{minValue}} and {{maxValue}}' + '{{name}} must be between {{minValue}} and {{maxValue}}' + => '{{name}} deve possuir de {{minValue}} a {{maxValue}} caracteres', + '{{name}} must be between {{minValue}} and {{maxValue}}' => '{{name}} deve possuir de {{minValue}} a {{maxValue}} caracteres', '{{name}} must be a valid telephone number for country {{countryName|trans}}' => '{{name}} deve ser um número de telefone válido para o país {{countryName|trans}}', @@ -25,10 +29,10 @@ Factory::setDefaultInstance( }) ); -exceptionFullMessage(static fn() => Validator::stringType()->length(2, 15)->phone('US')->assert(0)); +exceptionFullMessage(static fn() => Validator::stringType()->length(Validator::between(2, 15))->phone('US')->assert(0)); ?> --EXPECT-- - Todas as regras requeridas devem passar para 0 - 0 deve ser do tipo string - - 0 deve possuir de 2 a 15 caracteres + - O comprimento de 0 deve possuir de 2 a 15 caracteres - 0 deve ser um número de telefone válido para o país Estados Unidos diff --git a/tests/integration/translator-check.phpt b/tests/integration/translator-check.phpt index 9686237cc..39834da58 100644 --- a/tests/integration/translator-check.phpt +++ b/tests/integration/translator-check.phpt @@ -17,7 +17,7 @@ Factory::setDefaultInstance( }) ); -exceptionMessage(static fn() => Validator::stringType()->length(2, 15)->check(0)); +exceptionMessage(static fn() => Validator::stringType()->length(Validator::between(2, 15))->check(0)); ?> --EXPECT-- 0 deve ser do tipo string diff --git a/tests/unit/Rules/LengthTest.php b/tests/unit/Rules/LengthTest.php index c48f3aee2..23950abda 100644 --- a/tests/unit/Rules/LengthTest.php +++ b/tests/unit/Rules/LengthTest.php @@ -10,64 +10,38 @@ namespace Respect\Validation\Rules; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Test; -use Respect\Validation\Exceptions\ComponentException; -use Respect\Validation\Test\RuleTestCase; -use Respect\Validation\Test\Stubs\CountableStub; +use Respect\Validation\Test\Rules\Stub; +use Respect\Validation\Test\TestCase; -use function range; -use function tmpfile; +use function count; +use function mb_strlen; #[Group('rule')] #[CoversClass(Length::class)] -final class LengthTest extends RuleTestCase +final class LengthTest extends TestCase { #[Test] - public function isShouldNotNotAcceptInvalidLengths(): void + #[DataProvider('providerForStringTypes')] + public function itShouldValidateStringTypes(string $input): void { - $this->expectException(ComponentException::class); - $this->expectExceptionMessage('10 cannot be less than 1 for validation'); + $wrapped = Stub::pass(1); + $rule = new Length($wrapped); - new Length(10, 1); + self::assertValidInput($rule, $input); + self::assertEquals(mb_strlen($input), $wrapped->inputs[0]); } - /** @return iterable */ - public static function providerForValidInput(): iterable + #[Test] + #[DataProvider('providerForCountable')] + public function itShouldValidateCountable(mixed $input): void { - return [ - [new Length(1, 15), 'alganet'], - [new Length(4, 6), 'ççççç'], - [new Length(1, 30), range(1, 20)], - [new Length(0, 2), (object) ['foo' => 'bar', 'bar' => 'baz']], - [new Length(1, null), 'alganet'], //null is a valid max length, means "no maximum", - [new Length(null, 15), 'alganet'], //null is a valid min length, means "no minimum" - [new Length(1, 15, false), 'alganet'], - [new Length(4, 6, false), 'ççççç'], - [new Length(1, 30, false), range(1, 20)], - [new Length(1, 3, false), (object) ['foo' => 'bar', 'bar' => 'baz']], - [new Length(1, null, false), 'alganet'], //null is a valid max length, means "no maximum", - [new Length(null, 15, false), 'alganet'], //null is a valid min length, means "no minimum" - [new Length(1, 15), new CountableStub(5)], - [new Length(1, 15), 12345], - ]; - } + $wrapped = Stub::pass(1); + $rule = new Length($wrapped); - /** @return iterable */ - public static function providerForInvalidInput(): iterable - { - return [ - [new Length(1, 15), ''], - [new Length(1, 6), 'alganet'], - [new Length(1, 19), range(1, 20)], - [new Length(8, null), 'alganet'], //null is a valid max length, means "no maximum", - [new Length(null, 6), 'alganet'], //null is a valid min length, means "no minimum" - [new Length(1, 7, false), 'alganet'], - [new Length(3, 5, false), (object) ['foo' => 'bar', 'bar' => 'baz']], - [new Length(1, 30, false), range(1, 50)], - [new Length(0), tmpfile()], - [new Length(1, 4), new CountableStub(5)], - [new Length(1, 4), 12345], - ]; + self::assertValidInput($rule, $input); + self::assertEquals(count($input), $wrapped->inputs[0]); } }