Skip to content

Commit 6c3aed9

Browse files
committed
Create "Decimal" rule
Signed-off-by: Henrique Moody <[email protected]>
1 parent d532e94 commit 6c3aed9

File tree

7 files changed

+281
-0
lines changed

7 files changed

+281
-0
lines changed

docs/rules/Decimal.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Decimal
2+
3+
- `Decimal(int $decimals)`
4+
5+
Validates whether the input matches the expected number of decimals.
6+
7+
```php
8+
v::decimals(2)->validate('27990.50'); // true
9+
v::decimals(1)->validate('27990.50'); // false
10+
v::decimal(1)->validate(1.5); // true
11+
12+
```
13+
14+
## Known limitations
15+
16+
When validating float types, it is not possible to determine the amount of
17+
ending zeros and because of that, validations like the ones below will pass.
18+
19+
```php
20+
v::decimal(1)->validate(1.50); // true
21+
```
22+
23+
24+
## Categorization
25+
26+
- Numbers
27+
28+
## Changelog
29+
30+
Version | Description
31+
--------|-------------
32+
2.0.0 | Removed support to whitespaces by default
33+
0.5.0 | Renamed from `Digits` to `Digit`
34+
0.3.9 | Created as `Digits`
35+
36+
***
37+
See also:
38+
39+
- [Alnum](Alnum.md)
40+
- [Alpha](Alpha.md)
41+
- [Consonant](Consonant.md)
42+
- [CreditCard](CreditCard.md)
43+
- [Factor](Factor.md)
44+
- [Finite](Finite.md)
45+
- [Infinite](Infinite.md)
46+
- [IntType](IntType.md)
47+
- [IntVal](IntVal.md)
48+
- [NotEmoji](NotEmoji.md)
49+
- [NumericVal](NumericVal.md)
50+
- [Regex](Regex.md)
51+
- [Uuid](Uuid.md)
52+
- [Vowel](Vowel.md)
53+
- [Xdigit](Xdigit.md)

library/ChainedValidator.php

+2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ public function date(string $format = 'Y-m-d'): ChainedValidator;
101101

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

104+
public function decimal(int $decimals): ChainedValidator;
105+
104106
public function digit(string ...$additionalChars): ChainedValidator;
105107

106108
public function directory(): ChainedValidator;
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of Respect/Validation.
5+
*
6+
* (c) Alexandre Gomes Gaigalas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE file
9+
* that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Respect\Validation\Exceptions;
15+
16+
/**
17+
* @author Henrique Moody <[email protected]>
18+
*/
19+
final class DecimalException extends ValidationException
20+
{
21+
/**
22+
* {@inheritDoc}
23+
*/
24+
protected $defaultTemplates = [
25+
self::MODE_DEFAULT => [
26+
self::STANDARD => '{{name}} must have {{decimals}} decimals',
27+
],
28+
self::MODE_NEGATIVE => [
29+
self::STANDARD => '{{name}} must not have {{decimals}} decimals',
30+
],
31+
];
32+
}

library/Rules/Decimal.php

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
/*
4+
* This file is part of Respect/Validation.
5+
*
6+
* (c) Alexandre Gomes Gaigalas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE file
9+
* that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Respect\Validation\Rules;
15+
16+
use function is_numeric;
17+
use function is_string;
18+
use function number_format;
19+
use function preg_replace;
20+
use function var_export;
21+
22+
/**
23+
* Validates the decimal
24+
*
25+
* @author Henrique Moody <[email protected]>
26+
*/
27+
final class Decimal extends AbstractRule
28+
{
29+
/**
30+
* @var int
31+
*/
32+
private $decimals;
33+
34+
public function __construct(int $decimals)
35+
{
36+
$this->decimals = $decimals;
37+
}
38+
39+
/**
40+
* {@inheritDoc}
41+
*/
42+
public function validate($input): bool
43+
{
44+
if (!is_numeric($input)) {
45+
return false;
46+
}
47+
48+
return $this->toFormattedString($input) === $this->toRawString($input);
49+
}
50+
51+
/**
52+
* @param mixed $input
53+
*/
54+
private function toRawString($input): string
55+
{
56+
if (is_string($input)) {
57+
return $input;
58+
}
59+
60+
return var_export($input, true);
61+
}
62+
63+
/**
64+
* @param mixed $input
65+
*/
66+
private function toFormattedString($input): string
67+
{
68+
$formatted = number_format((float) $input, $this->decimals, '.', '');
69+
if (is_string($input)) {
70+
return $formatted;
71+
}
72+
73+
return preg_replace('/^(\d.\d)0*/', '$1', $formatted);
74+
}
75+
}

library/StaticValidator.php

+2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ public static function date(string $format = 'Y-m-d'): ChainedValidator;
101101

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

104+
public static function decimal(int $decimals): ChainedValidator;
105+
104106
public static function digit(string ...$additionalChars): ChainedValidator;
105107

106108
public static function directory(): ChainedValidator;

tests/integration/rules/decimal.phpt

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--CREDITS--
2+
Henrique Moody <[email protected]>
3+
--FILE--
4+
<?php
5+
6+
declare(strict_types=1);
7+
8+
require 'vendor/autoload.php';
9+
10+
use Respect\Validation\Exceptions\DecimalException;
11+
use Respect\Validation\Exceptions\NestedValidationException;
12+
use Respect\Validation\Validator as v;
13+
14+
try {
15+
v::decimal(3)->check(0.1234);
16+
} catch (DecimalException $exception) {
17+
echo $exception->getMessage() . PHP_EOL;
18+
}
19+
20+
try {
21+
v::decimal(2)->assert(0.123);
22+
} catch (NestedValidationException $exception) {
23+
echo $exception->getFullMessage() . PHP_EOL;
24+
}
25+
26+
try {
27+
v::not(v::decimal(5))->check(0.12345);
28+
} catch (DecimalException $exception) {
29+
echo $exception->getMessage() . PHP_EOL;
30+
}
31+
32+
try {
33+
v::not(v::decimal(2))->assert(0.34);
34+
} catch (NestedValidationException $exception) {
35+
echo $exception->getFullMessage() . PHP_EOL;
36+
}
37+
?>
38+
--EXPECT--
39+
0.1234 must have 3 decimals
40+
- 0.123 must have 2 decimals
41+
0.12345 must not have 5 decimals
42+
- 0.34 must not have 2 decimals

tests/unit/Rules/DecimalTest.php

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
/*
4+
* This file is part of Respect/Validation.
5+
*
6+
* (c) Alexandre Gomes Gaigalas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE file
9+
* that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Respect\Validation\Rules;
15+
16+
use Respect\Validation\Test\RuleTestCase;
17+
use stdClass;
18+
19+
use function acos;
20+
use function sqrt;
21+
22+
use const NAN;
23+
24+
/**
25+
* @group rule
26+
*
27+
* @covers \Respect\Validation\Rules\Decimal
28+
*
29+
* @author Henrique Moody <[email protected]>
30+
* @author Ismael Elias <[email protected]>
31+
* @author Vitaliy <[email protected]>
32+
*/
33+
final class DecimalTest extends RuleTestCase
34+
{
35+
/**
36+
* {@inheritDoc}
37+
*/
38+
public function providerForValidInput(): array
39+
{
40+
return [
41+
[new Decimal(0), 1],
42+
[new Decimal(1), 1.0],
43+
[new Decimal(2), 1.00],
44+
[new Decimal(3), 1.000],
45+
[new Decimal(2), 1.000],
46+
[new Decimal(1), 1.000],
47+
[new Decimal(2), '27990.50'],
48+
[new Decimal(1), 1.1],
49+
[new Decimal(1), '1.3'],
50+
[new Decimal(1), 1.50],
51+
[new Decimal(3), '1.000'],
52+
[new Decimal(3), 123456789.001],
53+
];
54+
}
55+
56+
/**
57+
* {@inheritDoc}
58+
*/
59+
public function providerForInvalidInput(): array
60+
{
61+
return [
62+
[new Decimal(1), '1.50'],
63+
[new Decimal(1), '27990.50'],
64+
[new Decimal(0), 2.0],
65+
[new Decimal(0), acos(1.01)],
66+
[new Decimal(0), sqrt(-1)],
67+
[new Decimal(0), NAN],
68+
[new Decimal(0), -NAN],
69+
[new Decimal(0), false],
70+
[new Decimal(0), true],
71+
[new Decimal(0), []],
72+
[new Decimal(0), new stdClass()],
73+
];
74+
}
75+
}

0 commit comments

Comments
 (0)