Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 1e4804f

Browse files
authored
Merge pull request #31 from programmatordev/1.x
1.x
2 parents 5d4e6c9 + 720af0a commit 1e4804f

15 files changed

+533
-10
lines changed

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
}
1313
],
1414
"require": {
15-
"php": ">=8.1"
15+
"php": ">=8.1",
16+
"symfony/intl": "^6.3",
17+
"symfony/polyfill-ctype": "^1.27"
1618
},
1719
"require-dev": {
1820
"phpunit/phpunit": "^10.0",

docs/03-rules.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
## Basic Rules
99

1010
- [NotBlank](03x-rules-not-blank.md)
11+
- [Type](03x-rules-type.md)
1112

1213
## Comparison Rules
1314

@@ -20,6 +21,7 @@
2021
## Choice Rules
2122

2223
- [Choice](03x-rules-choice.md)
24+
- [Country](03x-rules-country.md)
2325

2426
## Other Rules
2527

docs/03x-rules-choice.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Validator::choice(['red', 'green', 'blue'], multiple: true, minConstraint: 2, ma
4040
```
4141

4242
> **Note**
43-
> An `UnexpectedValueException` will be thrown when `multiple` is `true` and value to be validated is not an `array`.
43+
> An `UnexpectedValueException` will be thrown when `multiple` is `true` and the input value is not an `array`.
4444
4545
> **Note**
4646
> An `UnexpectedValueException` will be thrown when the `minConstraint` value is greater than or equal to the `maxConstraint` value.

docs/03x-rules-country.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Country
2+
3+
Validates that a value is a valid country code.
4+
5+
```php
6+
Country(
7+
string $code = 'alpha-2',
8+
string $message = 'The "{{ name }}" value is not a valid country code, "{{ value }}" given.'
9+
);
10+
```
11+
12+
## Basic Usage
13+
14+
```php
15+
// Default alpha-2 code
16+
Validator::country()->validate('PT'); // true
17+
Validator::country(code: 'alpha-2')->validate('PT'); // true
18+
19+
// Alpha-3 code
20+
Validator::country(code: 'alpha-3')->validate('PRT'); // true
21+
```
22+
23+
> **Note**
24+
> An `UnexpectedValueException` will be thrown when the `code` value is not a valid option.
25+
26+
> **Note**
27+
> An `UnexpectedValueException` will be thrown when the input value is not a `string`.
28+
29+
## Options
30+
31+
### `code`
32+
33+
type: `string` default: `alpha-2`
34+
35+
Set code type to validate the country.
36+
Check the [official country codes](https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes) list for more information.
37+
38+
Available options:
39+
40+
- `alpha-2`: two-letter code
41+
- `alpha-3`: three-letter code
42+
43+
### `message`
44+
45+
type: `string` default: `The "{{ name }}" value is not a valid country code, "{{ value }}" given.`
46+
47+
Message that will be shown if the input value is not a valid country code.
48+
49+
The following parameters are available:
50+
51+
| Parameter | Description |
52+
|---------------|---------------------------|
53+
| `{{ value }}` | The current invalid value |
54+
| `{{ name }}` | Name of the invalid value |
55+
| `{{ code }}` | Selected code type |

docs/03x-rules-type.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Type
2+
3+
Validates that a value is of a specific type.
4+
5+
If an array with multiple types is provided, it will validate if the value is of at least one of the given types.
6+
For example, if `['alpha', 'numeric']` is provided, it will validate if the value is of type `alpha` or of type `numeric`.
7+
8+
```php
9+
Type(
10+
string|array $constraint,
11+
string $message = 'The "{{ name }}" value should be of type "{{ constraint }}", "{{ value }}" given.'
12+
);
13+
```
14+
15+
## Basic Usage
16+
17+
```php
18+
// Single type
19+
Validator::type('string')->validate('green'); // true
20+
Validator::type('alphanumeric')->validate('gr33n'); // true
21+
22+
// Multiple types
23+
// Validates if value is of at least one of the provided types
24+
Validator::type(['alpha', 'numeric'])->validate('green'); // true (alpha)
25+
Validator::type(['alpha', 'numeric'])->validate('33'); // true (numeric)
26+
Validator::type(['alpha', 'numeric'])->validate('gr33n'); // false (not alpha nor numeric)
27+
28+
// Class or interface type
29+
Validator::type(\DateTime::class)->validate(new \DateTime()); // true
30+
Validator::type(\DateTimeInterface::class)->validate(new \DateTime()); // true
31+
```
32+
33+
> **Note**
34+
> An `UnexpectedValueException` will be thrown when a constraint type, class or interface is invalid.
35+
36+
## Options
37+
38+
### `constraint`
39+
40+
type: `string`|`array` `required`
41+
42+
Type(s) to validate the input value type.
43+
Can validate instances of classes and interfaces.
44+
45+
If an array with multiple types is provided, it will validate if the value is of at least one of the given types.
46+
For example, if `['alpha', 'numeric']` is provided, it will validate if the value is of type `alpha` or of type `numeric`.
47+
48+
Available data type constraints:
49+
50+
- [`bool`](https://www.php.net/manual/en/function.is-bool.php), [`boolean`](https://www.php.net/manual/en/function.is-bool.php)
51+
- [`int`](https://www.php.net/manual/en/function.is-int.php), [`integer`](https://www.php.net/manual/en/function.is-int.php), [`long`](https://www.php.net/manual/en/function.is-int.php)
52+
- [`float`](https://www.php.net/manual/en/function.is-float.php), [`double`](https://www.php.net/manual/en/function.is-float.php), [`real`](https://www.php.net/manual/en/function.is-float.php)
53+
- [`numeric`](https://www.php.net/manual/en/function.is-numeric.php)
54+
- [`string`](https://www.php.net/manual/en/function.is-string.php)
55+
- [`scalar`](https://www.php.net/manual/en/function.is-scalar.php)
56+
- [`array`](https://www.php.net/manual/en/function.is-array.php)
57+
- [`iterable`](https://www.php.net/manual/en/function.is-iterable.php)
58+
- [`countable`](https://www.php.net/manual/en/function.is-countable.php)
59+
- [`callable`](https://www.php.net/manual/en/function.is-callable.php)
60+
- [`object`](https://www.php.net/manual/en/function.is-object.php)
61+
- [`resource`](https://www.php.net/manual/en/function.is-resource.php)
62+
- [`null`](https://www.php.net/manual/en/function.is-null.php)
63+
64+
Available character type constraints:
65+
66+
- [`alphanumeric`](https://www.php.net/manual/en/function.ctype-alnum)
67+
- [`alpha`](https://www.php.net/manual/en/function.ctype-alpha.php)
68+
- [`digit`](https://www.php.net/manual/en/function.ctype-digit.php)
69+
- [`control`](https://www.php.net/manual/en/function.ctype-cntrl.php)
70+
- [`punctuation`](https://www.php.net/manual/en/function.ctype-punct.php)
71+
- [`hexadecimal`](https://www.php.net/manual/en/function.ctype-xdigit.php)
72+
- [`graph`](https://www.php.net/manual/en/function.ctype-graph.php)
73+
- [`printable`](https://www.php.net/manual/en/function.ctype-print.php)
74+
- [`whitespace`](https://www.php.net/manual/en/function.ctype-space.php)
75+
- [`lowercase`](https://www.php.net/manual/en/function.ctype-lower.php)
76+
- [`uppercase`](https://www.php.net/manual/en/function.ctype-upper.php)
77+
78+
### `message`
79+
80+
type `string` default: `The "{{ name }}" value should be of type "{{ constraint }}", "{{ value }}" given.`
81+
82+
Message that will be shown if input value is not of a specific type.
83+
84+
The following parameters are available:
85+
86+
| Parameter | Description |
87+
|--------------------|---------------------------|
88+
| `{{ value }}` | The current invalid value |
89+
| `{{ name }}` | Name of the invalid value |
90+
| `{{ constraint }}` | The valid type(s) |

src/ChainedValidatorInterface.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ public function choice(
3232
string $maxMessage = 'The "{{ name }}" value must have at most {{ maxConstraint }} choices, {{ numValues }} choices given.'
3333
): ChainedValidatorInterface;
3434

35+
public function country(
36+
string $code = 'alpha-2',
37+
string $message = 'The "{{ name }}" value is not a valid country code, "{{ value }}" given.'
38+
): ChainedValidatorInterface;
39+
3540
public function greaterThan(
3641
mixed $constraint,
3742
string $message = 'The "{{ name }}" value should be greater than "{{ constraint }}", "{{ value }}" given.'
@@ -64,4 +69,9 @@ public function range(
6469
): ChainedValidatorInterface;
6570

6671
public function rule(RuleInterface $constraint): ChainedValidatorInterface;
72+
73+
public function type(
74+
string|array $constraint,
75+
string $message = 'The "{{ name }}" value should be of type "{{ constraint }}", "{{ value }}" given.'
76+
): ChainedValidatorInterface;
6777
}

src/Exception/CountryException.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\YetAnotherPhpValidator\Exception;
4+
5+
class CountryException extends ValidationException {}

src/Exception/TypeException.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\YetAnotherPhpValidator\Exception;
4+
5+
class TypeException extends ValidationException {}

src/Exception/ValidationException.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ private function formatValue(mixed $value): string
3838
}
3939

4040
if (\is_string($value)) {
41-
return $value;
41+
// Replace line breaks and tabs with single space
42+
return str_replace(["\n", "\r", "\t", "\v", "\x00"], ' ', $value);
4243
}
4344

4445
if (\is_resource($value)) {

src/Rule/Country.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\YetAnotherPhpValidator\Rule;
4+
5+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\CountryException;
6+
use ProgrammatorDev\YetAnotherPhpValidator\Exception\UnexpectedValueException;
7+
use Symfony\Component\Intl\Countries;
8+
9+
class Country extends AbstractRule implements RuleInterface
10+
{
11+
public const ALPHA_2_CODE = 'alpha-2';
12+
public const ALPHA_3_CODE = 'alpha-3';
13+
14+
private const CODE_OPTIONS = [
15+
self::ALPHA_2_CODE,
16+
self::ALPHA_3_CODE
17+
];
18+
19+
public function __construct(
20+
private readonly string $code = self::ALPHA_2_CODE,
21+
private readonly string $message = 'The "{{ name }}" value is not a valid country code, "{{ value }}" given.'
22+
) {}
23+
24+
public function assert(mixed $value, string $name): void
25+
{
26+
if (!\in_array($this->code, self::CODE_OPTIONS)) {
27+
throw new UnexpectedValueException(
28+
\sprintf(
29+
'Invalid code "%s". Accepted values are: "%s".',
30+
$this->code,
31+
\implode(", ", self::CODE_OPTIONS)
32+
)
33+
);
34+
}
35+
36+
if (!\is_string($value)) {
37+
throw new UnexpectedValueException(
38+
\sprintf('Expected value of type "string", "%s" given.', get_debug_type($value))
39+
);
40+
}
41+
42+
// Keep original value for parameters
43+
$input = strtoupper($value);
44+
45+
if (
46+
($this->code === self::ALPHA_2_CODE && !Countries::exists($input))
47+
|| ($this->code === self::ALPHA_3_CODE && !Countries::alpha3CodeExists($input))
48+
) {
49+
throw new CountryException(
50+
message: $this->message,
51+
parameters: [
52+
'name' => $name,
53+
'value' => $value,
54+
'code' => $this->code
55+
]
56+
);
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)