Skip to content

Commit

Permalink
Create "Decimal" rule
Browse files Browse the repository at this point in the history
Signed-off-by: Henrique Moody <[email protected]>
  • Loading branch information
henriquemoody committed Oct 4, 2020
1 parent d532e94 commit 6c3aed9
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 0 deletions.
53 changes: 53 additions & 0 deletions docs/rules/Decimal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Decimal

- `Decimal(int $decimals)`

Validates whether the input matches the expected number of decimals.

```php
v::decimals(2)->validate('27990.50'); // true
v::decimals(1)->validate('27990.50'); // false
v::decimal(1)->validate(1.5); // true

```

## Known limitations

When validating float types, it is not possible to determine the amount of
ending zeros and because of that, validations like the ones below will pass.

```php
v::decimal(1)->validate(1.50); // true
```


## Categorization

- Numbers

## Changelog

Version | Description
--------|-------------
2.0.0 | Removed support to whitespaces by default
0.5.0 | Renamed from `Digits` to `Digit`
0.3.9 | Created as `Digits`

***
See also:

- [Alnum](Alnum.md)
- [Alpha](Alpha.md)
- [Consonant](Consonant.md)
- [CreditCard](CreditCard.md)
- [Factor](Factor.md)
- [Finite](Finite.md)
- [Infinite](Infinite.md)
- [IntType](IntType.md)
- [IntVal](IntVal.md)
- [NotEmoji](NotEmoji.md)
- [NumericVal](NumericVal.md)
- [Regex](Regex.md)
- [Uuid](Uuid.md)
- [Vowel](Vowel.md)
- [Xdigit](Xdigit.md)
2 changes: 2 additions & 0 deletions library/ChainedValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ public function date(string $format = 'Y-m-d'): ChainedValidator;

public function dateTime(?string $format = null): ChainedValidator;

public function decimal(int $decimals): ChainedValidator;

public function digit(string ...$additionalChars): ChainedValidator;

public function directory(): ChainedValidator;
Expand Down
32 changes: 32 additions & 0 deletions library/Exceptions/DecimalException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/*
* This file is part of Respect/Validation.
*
* (c) Alexandre Gomes Gaigalas <[email protected]>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

declare(strict_types=1);

namespace Respect\Validation\Exceptions;

/**
* @author Henrique Moody <[email protected]>
*/
final class DecimalException extends ValidationException
{
/**
* {@inheritDoc}
*/
protected $defaultTemplates = [
self::MODE_DEFAULT => [
self::STANDARD => '{{name}} must have {{decimals}} decimals',
],
self::MODE_NEGATIVE => [
self::STANDARD => '{{name}} must not have {{decimals}} decimals',
],
];
}
75 changes: 75 additions & 0 deletions library/Rules/Decimal.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

/*
* This file is part of Respect/Validation.
*
* (c) Alexandre Gomes Gaigalas <[email protected]>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

declare(strict_types=1);

namespace Respect\Validation\Rules;

use function is_numeric;
use function is_string;
use function number_format;
use function preg_replace;
use function var_export;

/**
* Validates the decimal
*
* @author Henrique Moody <[email protected]>
*/
final class Decimal extends AbstractRule
{
/**
* @var int
*/
private $decimals;

public function __construct(int $decimals)
{
$this->decimals = $decimals;
}

/**
* {@inheritDoc}
*/
public function validate($input): bool
{
if (!is_numeric($input)) {
return false;
}

return $this->toFormattedString($input) === $this->toRawString($input);
}

/**
* @param mixed $input
*/
private function toRawString($input): string
{
if (is_string($input)) {
return $input;
}

return var_export($input, true);
}

/**
* @param mixed $input
*/
private function toFormattedString($input): string
{
$formatted = number_format((float) $input, $this->decimals, '.', '');
if (is_string($input)) {
return $formatted;
}

return preg_replace('/^(\d.\d)0*/', '$1', $formatted);
}
}
2 changes: 2 additions & 0 deletions library/StaticValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ public static function date(string $format = 'Y-m-d'): ChainedValidator;

public static function dateTime(?string $format = null): ChainedValidator;

public static function decimal(int $decimals): ChainedValidator;

public static function digit(string ...$additionalChars): ChainedValidator;

public static function directory(): ChainedValidator;
Expand Down
42 changes: 42 additions & 0 deletions tests/integration/rules/decimal.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--CREDITS--
Henrique Moody <[email protected]>
--FILE--
<?php

declare(strict_types=1);

require 'vendor/autoload.php';

use Respect\Validation\Exceptions\DecimalException;
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Validator as v;

try {
v::decimal(3)->check(0.1234);
} catch (DecimalException $exception) {
echo $exception->getMessage() . PHP_EOL;
}

try {
v::decimal(2)->assert(0.123);
} catch (NestedValidationException $exception) {
echo $exception->getFullMessage() . PHP_EOL;
}

try {
v::not(v::decimal(5))->check(0.12345);
} catch (DecimalException $exception) {
echo $exception->getMessage() . PHP_EOL;
}

try {
v::not(v::decimal(2))->assert(0.34);
} catch (NestedValidationException $exception) {
echo $exception->getFullMessage() . PHP_EOL;
}
?>
--EXPECT--
0.1234 must have 3 decimals
- 0.123 must have 2 decimals
0.12345 must not have 5 decimals
- 0.34 must not have 2 decimals
75 changes: 75 additions & 0 deletions tests/unit/Rules/DecimalTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

/*
* This file is part of Respect/Validation.
*
* (c) Alexandre Gomes Gaigalas <[email protected]>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

declare(strict_types=1);

namespace Respect\Validation\Rules;

use Respect\Validation\Test\RuleTestCase;
use stdClass;

use function acos;
use function sqrt;

use const NAN;

/**
* @group rule
*
* @covers \Respect\Validation\Rules\Decimal
*
* @author Henrique Moody <[email protected]>
* @author Ismael Elias <[email protected]>
* @author Vitaliy <[email protected]>
*/
final class DecimalTest extends RuleTestCase
{
/**
* {@inheritDoc}
*/
public function providerForValidInput(): array
{
return [
[new Decimal(0), 1],
[new Decimal(1), 1.0],
[new Decimal(2), 1.00],
[new Decimal(3), 1.000],
[new Decimal(2), 1.000],
[new Decimal(1), 1.000],
[new Decimal(2), '27990.50'],
[new Decimal(1), 1.1],
[new Decimal(1), '1.3'],
[new Decimal(1), 1.50],
[new Decimal(3), '1.000'],
[new Decimal(3), 123456789.001],
];
}

/**
* {@inheritDoc}
*/
public function providerForInvalidInput(): array
{
return [
[new Decimal(1), '1.50'],
[new Decimal(1), '27990.50'],
[new Decimal(0), 2.0],
[new Decimal(0), acos(1.01)],
[new Decimal(0), sqrt(-1)],
[new Decimal(0), NAN],
[new Decimal(0), -NAN],
[new Decimal(0), false],
[new Decimal(0), true],
[new Decimal(0), []],
[new Decimal(0), new stdClass()],
];
}
}

0 comments on commit 6c3aed9

Please sign in to comment.