Skip to content

Commit c32f857

Browse files
committed
Add value validation using rules #1
1 parent 6057810 commit c32f857

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+2048
-97
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ help with the documentation, new features, tests).
1717

1818
- 🚀 Retrieve values from Array (JSON) / XML with correct return type
1919
- 🏆 Makes PHPStan / IDE happy due the return types
20-
- 🤹‍ Basic type without additional looping.
20+
- 🤹‍ Ensures that desired value is in correct type (without additional loop validation. Validates always on get).
2121

2222
```php
2323
$data = new \Wrkflow\GetValue\GetValue(new \Wrkflow\GetValue\DataHolders\ArrayData([
@@ -27,7 +27,7 @@ $data = new \Wrkflow\GetValue\GetValue(new \Wrkflow\GetValue\DataHolders\ArrayDa
2727
['name' => 'test 2', 'tags' => ['test']],
2828
],
2929
]));
30-
$page = $data->getRequiredInt('page'); // Will throw MissingValueForKeyException
30+
$page = $data->getRequiredInt('page'); // Will throw MissingValueForKeyException if the `page` is not present
3131
$items = $data->getRequiredArray('items');
3232

3333
foreach ($items as $item) {

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"phpstan/phpstan-phpunit": "^1.1.1",
1616
"phpunit/phpunit": "^9.5.20",
1717
"rector/rector": "^0.12.22",
18-
"symplify/easy-coding-standard": "^10.2.2"
18+
"symplify/easy-coding-standard": "^10.2.2",
19+
"ext-simplexml": "*"
1920
},
2021
"scripts": {
2122
"check": "composer lint && composer test",

docs/content/en/customization/custom-exceptions.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ class CustomExceptionBuilder extends
2828
{
2929
return new NotAnArrayException($key, $this->customLogContext);
3030
}
31+
32+
/**
33+
* @param class-string<RuleContract> $ruleClassName
34+
*/
35+
public function validationFailed(string $key, string $ruleClassName): Exception;
36+
{
37+
return new ValidationFailedException($key, $this->customLogContext);
38+
}
3139
}
3240
```
3341

docs/content/en/index.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ position: 1
1111

1212
- 🚀 Retrieve values from Array (JSON) / XML with correct return type
1313
- 🏆 Makes PHPStan / IDE happy due the return types
14-
- 🤹‍ Basic type without additional looping.
14+
- 🤹‍ Ensures that desired value is in correct type (without additional loop validation. Validates always on get).
1515

1616
## Installation
1717

@@ -33,7 +33,10 @@ Simple build **GetValue** class with **ArrayData** class that will expose the da
3333
```php
3434
$data = new \Wrkflow\GetValue\GetValue(new \Wrkflow\GetValue\DataHolders\ArrayData([
3535
'page' => 1,
36-
'items' => [['name' => 'test', 'tags' => null, 'label' => 'yes'], ['name' => 'test 2', 'tags' => ['test']]]
36+
'items' => [
37+
['name' => 'test', 'tags' => null, 'label' => 'yes'],
38+
['name' => 'test 2', 'tags' => ['test'],]
39+
],
3740
]));
3841
```
3942

@@ -42,22 +45,28 @@ $data = new \Wrkflow\GetValue\GetValue(new \Wrkflow\GetValue\DataHolders\ArrayDa
4245
Simple build **GetValue** class with **XMLData** class that will expose the data.
4346

4447
```php
45-
$data = new \Wrkflow\GetValue\GetValue(new \Wrkflow\GetValue\DataHolders\XMLData(new SimpleXMLElement('<root><title>test</title><test attribute="test"/></root>')));
48+
$simpleXMLElement = new SimpleXMLElement('<root><title>test</title><test attribute="test"/></root>');
49+
$data = new \Wrkflow\GetValue\GetValue(new \Wrkflow\GetValue\DataHolders\XMLData($simpleXMLElement));
4650
```
4751

4852
## Values
4953

54+
> All values are validated within its type definition (int will be checked by IntegerRule, string by StringRule, etc).
55+
5056
For getting values there are always 2 methods:
5157

5258
- get nullable value
5359
- get required value
5460

61+
You can additionally add validation rules (as second parameter) to ensure you will get correct value.
62+
Check [Validation documentation](/validation) for more.
63+
5564
### Int
5665

5766
Get nullable int.
5867

5968
```php
60-
$value = $data->getInt('key');
69+
$value = $data->getInt('key', rules: [new \Wrkflow\GetValue\Rules\MinRule(0)]);
6170
```
6271

6372
Get required int value. Throws `MissingValueForKeyException` exception if missing.

docs/content/en/validation.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
title: Validation
3+
subtitle: 'Validate the value is not null.'
4+
position: 2
5+
---
6+
7+
## Rules
8+
9+
- All rules can be combined (all must be successful).
10+
- `get` methods has `rules` parameter. Check IDE autocomplete.
11+
- You can create your own rules by extending `Wrkflow\GetValue\Contracts\RuleContract`
12+
- You are free to add more rules to this package.
13+
14+
### AlphaDashRule
15+
16+
Checks if the string contains only digits/alphabet/-/_.
17+
18+
### AlphaNumericRule
19+
20+
Checks if the value is valid IP (using [FILTER_VALIDATE_IP](https://www.php.net/manual/en/filter.filters.flags.php))
21+
22+
### ArrayRule
23+
24+
Checks if the given value is an array. Not needed if using `getArray*` methods.
25+
26+
### BetweenRule
27+
28+
> Uses SizeRule
29+
30+
Checks if the array|float|int|bool|string|null is within max/min (int/float).
31+
32+
### BooleanRule
33+
34+
Checks if the value is a boolean.
35+
36+
### EmailRule
37+
38+
Checks if the value is valid e-mail (
39+
using [FILTER_VALIDATE_EMAIL](https://www.php.net/manual/en/filter.filters.flags.php))
40+
41+
### FloatRule
42+
43+
Checks if the value is a float.
44+
45+
### IntegerRule
46+
47+
Checks if the value is a integer.
48+
49+
### IpRule
50+
51+
Checks if the value is valid IP (using [FILTER_VALIDATE_IP](https://www.php.net/manual/en/filter.filters.flags.php))
52+
53+
### MaxRule
54+
55+
> Uses SizeRule
56+
57+
Checks if the array|float|int|bool|string|null is equal or greater than given int/float value.
58+
59+
### MinRule
60+
61+
Checks if the array|float|int|bool|string|null is equal or lower than given int/float value.
62+
63+
### NumericRule
64+
65+
Checks if the value is a numeric.
66+
67+
### RegexRule
68+
69+
Checks if value is valid against given regex.
70+
71+
### SizeRule
72+
73+
Checks if:
74+
75+
- array - count of items equals to given value
76+
- float, int - equals to given value
77+
- bool - equals to given value 1 (true) or 0 (false)
78+
- string - length of string equals to given value
79+
80+
### StringRule
81+
82+
Checks if value is valid string.
83+
84+
### UrlRule
85+
86+
Checks if the value is valid URL (using [FILTER_VALIDATE_URL](https://www.php.net/manual/en/filter.filters.flags.php))
87+
88+
## Notes
89+
90+
- Rules code was extracted from [laurynasgadl/php-validator](https://github.com/laurynasgadl/php-validator) and PHPStan
91+
8 ready. First I wanted to re-use existing package but did not find.
92+
- I'm thinking of that I will move rules to its own package for more reusability.

rector.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
declare(strict_types=1);
44

55
use Rector\CodingStyle\Rector\ClassConst\VarConstantCommentRector;
6-
use Rector\CodingStyle\Rector\ClassMethod\UnSpreadOperatorRector;
76
use Rector\Config\RectorConfig;
87
use Rector\Core\ValueObject\PhpVersion;
98
use Rector\Set\ValueObject\LevelSetList;
109
use Rector\Set\ValueObject\SetList;
1110
use Rector\Strict\Rector\AbstractFalsyScalarRuleFixerRector;
1211
use Rector\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector;
12+
use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector;
1313

1414
return static function (RectorConfig $config): void {
1515
$config->paths([__DIR__ . '/src', __DIR__ . '/tests']);
@@ -27,10 +27,10 @@
2727
AbstractFalsyScalarRuleFixerRector::TREAT_AS_NON_EMPTY => false,
2828
]
2929
);
30+
$config->ruleWithConfiguration(AddVoidReturnTypeWhereNoReturnRector::class, [
31+
AddVoidReturnTypeWhereNoReturnRector::USE_PHPDOC => false,
32+
]);
3033

3134
// SKIP laravel
32-
$config->skip([
33-
UnSpreadOperatorRector::class => [__DIR__ . '/src/Testing/Laravel/TestingApplication.php'],
34-
VarConstantCommentRector::class,
35-
]);
35+
$config->skip([VarConstantCommentRector::class]);
3636
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValue\Actions;
6+
7+
use Wrkflow\GetValue\Contracts\ExceptionBuilderContract;
8+
use Wrkflow\GetValue\Contracts\RuleContract;
9+
use Wrkflow\GetValue\DataHolders\AbstractData;
10+
11+
class GetValidatedValueAction
12+
{
13+
public function __construct(protected readonly ExceptionBuilderContract $exceptionBuilder)
14+
{
15+
}
16+
17+
/**
18+
* @param array<RuleContract> $rules
19+
*/
20+
public function execute(string $key, AbstractData $data, array $rules): mixed
21+
{
22+
$value = $data->getValue($key);
23+
24+
if ($rules === []) {
25+
return $value;
26+
}
27+
28+
// Skip validation on null value
29+
if ($value === null) {
30+
return $value;
31+
}
32+
33+
foreach ($rules as $rule) {
34+
if ($rule->passes($value) === false) {
35+
throw $this->exceptionBuilder->validationFailed($key, $rule::class);
36+
}
37+
}
38+
39+
return $value;
40+
}
41+
}

src/Builders/ExceptionBuilder.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Wrkflow\GetValue\Exceptions\ArrayIsEmptyException;
1010
use Wrkflow\GetValue\Exceptions\MissingValueForKeyException;
1111
use Wrkflow\GetValue\Exceptions\NotAnArrayException;
12+
use Wrkflow\GetValue\Exceptions\ValidationFailedException;
1213

1314
class ExceptionBuilder implements ExceptionBuilderContract
1415
{
@@ -26,4 +27,12 @@ public function notAnArray(string $key): Exception
2627
{
2728
return new NotAnArrayException($key);
2829
}
30+
31+
public function validationFailed(string $key, string $ruleClassName): Exception
32+
{
33+
$classNameParts = explode('\\', $ruleClassName);
34+
$shortClassName = end($classNameParts);
35+
36+
return new ValidationFailedException($key, $shortClassName . ' failed');
37+
}
2938
}

src/Contracts/ExceptionBuilderContract.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,9 @@ public function missingValue(string $key): Exception;
1313
public function arrayIsEmpty(string $key): Exception;
1414

1515
public function notAnArray(string $key): Exception;
16+
17+
/**
18+
* @param class-string<RuleContract> $ruleClassName
19+
*/
20+
public function validationFailed(string $key, string $ruleClassName): Exception;
1621
}

src/Contracts/RuleContract.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValue\Contracts;
6+
7+
interface RuleContract
8+
{
9+
public function passes(mixed $value): bool;
10+
}

0 commit comments

Comments
 (0)