Skip to content

Commit

Permalink
Turn Length into a transformation
Browse files Browse the repository at this point in the history
Signed-off-by: Henrique Moody <[email protected]>
  • Loading branch information
henriquemoody committed Mar 19, 2024
1 parent b2250c6 commit b5eb196
Show file tree
Hide file tree
Showing 30 changed files with 374 additions and 296 deletions.
2 changes: 2 additions & 0 deletions docs/08-list-of-rules-by-category.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -260,6 +261,7 @@

- [Call](rules/Call.md)
- [Each](rules/Each.md)
- [Length](rules/Length.md)
- [Max](rules/Max.md)
- [Min](rules/Min.md)

Expand Down
1 change: 1 addition & 0 deletions docs/rules/GreaterThan.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
49 changes: 20 additions & 29 deletions docs/rules/Length.md
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions docs/rules/LessThan.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
1 change: 1 addition & 0 deletions docs/rules/LessThanOrEqual.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions docs/rules/Max.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions docs/rules/Min.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion library/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -64,7 +65,7 @@ public function __construct()
new DeprecatedKey(
new DeprecatedKeyValue(
new DeprecatedMinAndMax(
new DeprecatedKeyNested(new DeprecatedType())
new DeprecatedKeyNested(new DeprecatedLength(new DeprecatedType()))
)
)
)
Expand Down
2 changes: 1 addition & 1 deletion library/Mixins/ChainedValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion library/Mixins/StaticValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
9 changes: 7 additions & 2 deletions library/Rules/Domain.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
144 changes: 18 additions & 126 deletions library/Rules/Length.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, mixed>
*/
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<mixed>|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);
}
}
Loading

0 comments on commit b5eb196

Please sign in to comment.