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

Commit e32b2dc

Browse files
authored
Merge pull request #67 from programmatordev/1.x
1.x
2 parents a9ed4e6 + 52ce0e7 commit e32b2dc

35 files changed

+445
-86
lines changed

composer.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "programmatordev/yet-another-php-validator",
33
"description": "PHP validator with expressive error messages",
44
"type": "library",
5-
"keywords": ["PHP", "PHP8", "Validator", "Validation"],
5+
"keywords": ["php", "php8", "validator", "validation"],
66
"license": "MIT",
77
"authors": [
88
{
@@ -14,13 +14,14 @@
1414
"require": {
1515
"php": ">=8.1",
1616
"egulias/email-validator": "^4.0",
17-
"symfony/intl": "^6.3",
17+
"symfony/intl": "^6.4",
1818
"symfony/polyfill-ctype": "^1.27",
19-
"symfony/polyfill-intl-grapheme": "^1.29"
19+
"symfony/polyfill-intl-grapheme": "^1.29",
20+
"symfony/polyfill-intl-icu": "^1.29"
2021
},
2122
"require-dev": {
2223
"phpunit/phpunit": "^10.0",
23-
"symfony/var-dumper": "^6.3"
24+
"symfony/var-dumper": "^6.4"
2425
},
2526
"autoload": {
2627
"psr-4": {

docs/03-rules.md

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939

4040
- [Choice](03-rules_choice.md)
4141
- [Country](03-rules_country.md)
42+
- [Language](03-rules_language.md)
43+
- [Locale](03-rules_locale.md)
4244

4345
## Iterable Rules
4446

docs/03-rules_country.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Available options:
4141

4242
### `message`
4343

44-
type: `?string` default: `The {{ name }} value is not a valid {{ code }} country code, {{ value }} given.`
44+
type: `?string` default: `The {{ name }} value is not a valid country, {{ value }} given.`
4545

4646
Message that will be shown if the input value is not a valid country code.
4747

docs/03-rules_language.md

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

docs/03-rules_locale.md

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Locale
2+
3+
Validates that a value is a valid locale code.
4+
5+
```php
6+
Locale(
7+
bool $canonicalize = false,
8+
?string $message = null
9+
);
10+
```
11+
12+
## Basic Usage
13+
14+
```php
15+
// by default, code should be the language code (pt, en, ...)
16+
// or the language code followed by an underscore and the uppercased country code (pt_PT, en_US, ...)
17+
Validator::locale()->validate('pt'); // true
18+
Validator::locale()->validate('pt_PT'); // true
19+
Validator::locale()->validate('pt_pt'); // false
20+
Validator::locale()->validate('pt-PT'); // false
21+
Validator::locale()->validate('pt_PRT'); // false
22+
23+
// canonicalize value before validation
24+
Validator::language(canonicalize: true)->validate('pt_pt'); // true
25+
Validator::language(canonicalize: true)->validate('pt-PT'); // true
26+
Validator::language(canonicalize: true)->validate('pt_PRT'); // true
27+
Validator::language(canonicalize: true)->validate('PT-pt.utf8'); // true
28+
```
29+
30+
> [!NOTE]
31+
> An `UnexpectedValueException` will be thrown when the input value is not a `string`.
32+
33+
## Options
34+
35+
### `canonicalize`
36+
37+
type: `bool` default: `false`
38+
39+
If `true`, the input value will be normalized before validation, according to the following [documentation](https://unicode-org.github.io/icu/userguide/locale/#canonicalization).
40+
41+
### `message`
42+
43+
type: `?string` default: `The {{ name }} value is not a valid locale, {{ value }} given.`
44+
45+
Message that will be shown if the input value is not a valid locale code.
46+
47+
The following parameters are available:
48+
49+
| Parameter | Description |
50+
|---------------|---------------------------|
51+
| `{{ value }}` | The current invalid value |
52+
| `{{ name }}` | Name of the invalid value |
53+
54+
## Changelog
55+
56+
- `1.1.0` Created

src/ChainedValidatorInterface.php

+10
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ public function greaterThanOrEqual(
7070
?string $message = null
7171
): ChainedValidatorInterface&Validator;
7272

73+
public function language(
74+
string $code = 'alpha-2',
75+
?string $message = null
76+
): ChainedValidatorInterface&Validator;
77+
7378
public function length(
7479
?int $min = null,
7580
?int $max = null,
@@ -92,6 +97,11 @@ public function lessThanOrEqual(
9297
?string $message = null
9398
): ChainedValidatorInterface&Validator;
9499

100+
public function locale(
101+
bool $canonicalize = false,
102+
?string $message = null
103+
): ChainedValidatorInterface&Validator;
104+
95105
public function notBlank(
96106
?callable $normalizer = null,
97107
?string $message = null

src/Exception/LanguageException.php

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Exception;
4+
5+
class LanguageException extends ValidationException {}

src/Exception/LocaleException.php

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Exception;
4+
5+
class LocaleException extends ValidationException {}

src/Rule/Country.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Country extends AbstractRule implements RuleInterface
1717
self::ALPHA_3_CODE
1818
];
1919

20-
private string $message = 'The {{ name }} value is not a valid {{ code }} country code, {{ value }} given.';
20+
private string $message = 'The {{ name }} value is not a valid country, {{ value }} given.';
2121

2222
public function __construct(
2323
private readonly string $code = self::ALPHA_2_CODE,
@@ -37,8 +37,8 @@ public function assert(mixed $value, ?string $name = null): void
3737
throw new UnexpectedTypeException('string', get_debug_type($value));
3838
}
3939

40-
// Keep original value for parameters
41-
$input = strtoupper($value);
40+
// keep original value for parameters
41+
$input = \strtoupper($value);
4242

4343
if (
4444
($this->code === self::ALPHA_2_CODE && !Countries::exists($input))

src/Rule/Language.php

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Rule;
4+
5+
use ProgrammatorDev\Validator\Exception\LanguageException;
6+
use ProgrammatorDev\Validator\Exception\UnexpectedOptionException;
7+
use ProgrammatorDev\Validator\Exception\UnexpectedTypeException;
8+
use Symfony\Component\Intl\Languages;
9+
10+
class Language extends AbstractRule implements RuleInterface
11+
{
12+
public const ALPHA_2_CODE = 'alpha-2';
13+
public const ALPHA_3_CODE = 'alpha-3';
14+
15+
private const CODE_OPTIONS = [
16+
self::ALPHA_2_CODE,
17+
self::ALPHA_3_CODE
18+
];
19+
20+
private string $message = 'The {{ name }} value is not a valid language, {{ value }} given.';
21+
22+
public function __construct(
23+
private readonly string $code = self::ALPHA_2_CODE,
24+
?string $message = null
25+
)
26+
{
27+
$this->message = $message ?? $this->message;
28+
}
29+
30+
public function assert(mixed $value, ?string $name = null): void
31+
{
32+
if (!\in_array($this->code, self::CODE_OPTIONS)) {
33+
throw new UnexpectedOptionException('code', self::CODE_OPTIONS, $this->code);
34+
}
35+
36+
if (!\is_string($value)) {
37+
throw new UnexpectedTypeException('string', get_debug_type($value));
38+
}
39+
40+
// keep original value for parameters
41+
$input = \strtolower($value);
42+
43+
if (
44+
($this->code === self::ALPHA_2_CODE && !Languages::exists($input))
45+
|| ($this->code === self::ALPHA_3_CODE && !Languages::alpha3CodeExists($input))
46+
) {
47+
throw new LanguageException(
48+
message: $this->message,
49+
parameters: [
50+
'name' => $name,
51+
'value' => $value,
52+
'code' => $this->code
53+
]
54+
);
55+
}
56+
}
57+
}

src/Rule/Locale.php

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Rule;
4+
5+
use ProgrammatorDev\Validator\Exception\LocaleException;
6+
use ProgrammatorDev\Validator\Exception\UnexpectedTypeException;
7+
use Symfony\Component\Intl\Locales;
8+
9+
class Locale extends AbstractRule implements RuleInterface
10+
{
11+
private string $message = 'The {{ name }} value is not a valid locale, {{ value }} given.';
12+
13+
public function __construct(
14+
private readonly bool $canonicalize = false,
15+
?string $message = null
16+
)
17+
{
18+
$this->message = $message ?? $this->message;
19+
}
20+
21+
public function assert(mixed $value, ?string $name = null): void
22+
{
23+
if (!\is_string($value)) {
24+
throw new UnexpectedTypeException('string', get_debug_type($value));
25+
}
26+
27+
// keep original value for parameters
28+
$input = $value;
29+
30+
if ($this->canonicalize) {
31+
$input = \Locale::canonicalize($input);
32+
}
33+
34+
if (!Locales::exists($input)) {
35+
throw new LocaleException(
36+
message: $this->message,
37+
parameters: [
38+
'name' => $name,
39+
'value' => $value
40+
]
41+
);
42+
}
43+
}
44+
}

src/StaticValidatorInterface.php

+10
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ public static function greaterThanOrEqual(
6969
?string $message = null
7070
): ChainedValidatorInterface&Validator;
7171

72+
public static function language(
73+
string $code = 'alpha-2',
74+
?string $message = null
75+
): ChainedValidatorInterface&Validator;
76+
7277
public static function length(
7378
?int $min = null,
7479
?int $max = null,
@@ -91,6 +96,11 @@ public static function lessThanOrEqual(
9196
?string $message = null
9297
): ChainedValidatorInterface&Validator;
9398

99+
public static function locale(
100+
bool $canonicalize = false,
101+
?string $message = null
102+
): ChainedValidatorInterface&Validator;
103+
94104
public static function notBlank(
95105
?callable $normalizer = null,
96106
?string $message = null

tests/ChoiceTest.php

+8-8
Original file line numberDiff line numberDiff line change
@@ -66,39 +66,39 @@ public static function provideRuleMessageOptionData(): \Generator
6666
yield 'message' => [
6767
new Choice(
6868
constraints: $constraints,
69-
message: 'The {{ name }} value {{ value }} is not a valid choice.'
69+
message: '{{ name }} | {{ value }} | {{ constraints }}'
7070
),
7171
10,
72-
'The test value 10 is not a valid choice.'
72+
'test | 10 | [1, 2, 3, 4, 5]'
7373
];
7474
yield 'multiple message' => [
7575
new Choice(
7676
constraints: $constraints,
7777
multiple: true,
78-
multipleMessage: 'The {{ name }} value {{ value }} is not a valid choice.'
78+
multipleMessage: '{{ name }} | {{ value }} | {{ constraints }}'
7979
),
8080
[10],
81-
'The test value [10] is not a valid choice.'
81+
'test | [10] | [1, 2, 3, 4, 5]'
8282
];
8383
yield 'min message' => [
8484
new Choice(
8585
constraints: $constraints,
8686
multiple: true,
8787
min: 2,
88-
minMessage: 'The {{ name }} value should have at least {{ min }} choices.'
88+
minMessage: '{{ name }} | {{ value }} | {{ constraints }} | {{ min }} | {{ max }} | {{ numElements }}'
8989
),
9090
[1],
91-
'The test value should have at least 2 choices.'
91+
'test | [1] | [1, 2, 3, 4, 5] | 2 | null | 1'
9292
];
9393
yield 'max message' => [
9494
new Choice(
9595
constraints: $constraints,
9696
multiple: true,
9797
max: 2,
98-
maxMessage: 'The {{ name }} value should have at most {{ max }} choices.'
98+
maxMessage: '{{ name }} | {{ value }} | {{ constraints }} | {{ min }} | {{ max }} | {{ numElements }}'
9999
),
100100
[1, 2, 3],
101-
'The test value should have at most 2 choices.'
101+
'test | [1, 2, 3] | [1, 2, 3, 4, 5] | null | 2 | 3'
102102
];
103103
}
104104

0 commit comments

Comments
 (0)