Skip to content

Commit

Permalink
Merge pull request #67 from programmatordev/1.x
Browse files Browse the repository at this point in the history
1.x
  • Loading branch information
andrepimpao committed Apr 30, 2024
2 parents a9ed4e6 + 52ce0e7 commit e32b2dc
Show file tree
Hide file tree
Showing 35 changed files with 445 additions and 86 deletions.
9 changes: 5 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "programmatordev/yet-another-php-validator",
"description": "PHP validator with expressive error messages",
"type": "library",
"keywords": ["PHP", "PHP8", "Validator", "Validation"],
"keywords": ["php", "php8", "validator", "validation"],
"license": "MIT",
"authors": [
{
Expand All @@ -14,13 +14,14 @@
"require": {
"php": ">=8.1",
"egulias/email-validator": "^4.0",
"symfony/intl": "^6.3",
"symfony/intl": "^6.4",
"symfony/polyfill-ctype": "^1.27",
"symfony/polyfill-intl-grapheme": "^1.29"
"symfony/polyfill-intl-grapheme": "^1.29",
"symfony/polyfill-intl-icu": "^1.29"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
"symfony/var-dumper": "^6.3"
"symfony/var-dumper": "^6.4"
},
"autoload": {
"psr-4": {
Expand Down
2 changes: 2 additions & 0 deletions docs/03-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

- [Choice](03-rules_choice.md)
- [Country](03-rules_country.md)
- [Language](03-rules_language.md)
- [Locale](03-rules_locale.md)

## Iterable Rules

Expand Down
2 changes: 1 addition & 1 deletion docs/03-rules_country.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Available options:

### `message`

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

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

Expand Down
58 changes: 58 additions & 0 deletions docs/03-rules_language.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Language

Validates that a value is a valid language code.

```php
Language(
string $code = 'alpha-2',
?string $message = null
);
```

## Basic Usage

```php
// default alpha-2 code
Validator::language()->validate('pt'); // true

// alpha-3 code
Validator::language(code: 'alpha-3')->validate('por'); // true
```

> [!NOTE]
> An `UnexpectedValueException` will be thrown when the `code` value is not a valid option.
> [!NOTE]
> An `UnexpectedValueException` will be thrown when the input value is not a `string`.
## Options

### `code`

type: `string` default: `alpha-2`

Set code type to validate the language.
Check the [official language codes](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes) list for more information.

Available options:

- `alpha-2`: two-letter code
- `alpha-3`: three-letter code

### `message`

type: `?string` default: `The {{ name }} value is not a valid language, {{ value }} given.`

Message that will be shown if the input value is not a valid language code.

The following parameters are available:

| Parameter | Description |
|---------------|---------------------------|
| `{{ value }}` | The current invalid value |
| `{{ name }}` | Name of the invalid value |
| `{{ code }}` | Selected code type |

## Changelog

- `1.1.0` Created
56 changes: 56 additions & 0 deletions docs/03-rules_locale.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Locale

Validates that a value is a valid locale code.

```php
Locale(
bool $canonicalize = false,
?string $message = null
);
```

## Basic Usage

```php
// by default, code should be the language code (pt, en, ...)
// or the language code followed by an underscore and the uppercased country code (pt_PT, en_US, ...)
Validator::locale()->validate('pt'); // true
Validator::locale()->validate('pt_PT'); // true
Validator::locale()->validate('pt_pt'); // false
Validator::locale()->validate('pt-PT'); // false
Validator::locale()->validate('pt_PRT'); // false

// canonicalize value before validation
Validator::language(canonicalize: true)->validate('pt_pt'); // true
Validator::language(canonicalize: true)->validate('pt-PT'); // true
Validator::language(canonicalize: true)->validate('pt_PRT'); // true
Validator::language(canonicalize: true)->validate('PT-pt.utf8'); // true
```

> [!NOTE]
> An `UnexpectedValueException` will be thrown when the input value is not a `string`.
## Options

### `canonicalize`

type: `bool` default: `false`

If `true`, the input value will be normalized before validation, according to the following [documentation](https://unicode-org.github.io/icu/userguide/locale/#canonicalization).

### `message`

type: `?string` default: `The {{ name }} value is not a valid locale, {{ value }} given.`

Message that will be shown if the input value is not a valid locale code.

The following parameters are available:

| Parameter | Description |
|---------------|---------------------------|
| `{{ value }}` | The current invalid value |
| `{{ name }}` | Name of the invalid value |

## Changelog

- `1.1.0` Created
10 changes: 10 additions & 0 deletions src/ChainedValidatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public function greaterThanOrEqual(
?string $message = null
): ChainedValidatorInterface&Validator;

public function language(
string $code = 'alpha-2',
?string $message = null
): ChainedValidatorInterface&Validator;

public function length(
?int $min = null,
?int $max = null,
Expand All @@ -92,6 +97,11 @@ public function lessThanOrEqual(
?string $message = null
): ChainedValidatorInterface&Validator;

public function locale(
bool $canonicalize = false,
?string $message = null
): ChainedValidatorInterface&Validator;

public function notBlank(
?callable $normalizer = null,
?string $message = null
Expand Down
5 changes: 5 additions & 0 deletions src/Exception/LanguageException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace ProgrammatorDev\Validator\Exception;

class LanguageException extends ValidationException {}
5 changes: 5 additions & 0 deletions src/Exception/LocaleException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace ProgrammatorDev\Validator\Exception;

class LocaleException extends ValidationException {}
6 changes: 3 additions & 3 deletions src/Rule/Country.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Country extends AbstractRule implements RuleInterface
self::ALPHA_3_CODE
];

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

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

// Keep original value for parameters
$input = strtoupper($value);
// keep original value for parameters
$input = \strtoupper($value);

if (
($this->code === self::ALPHA_2_CODE && !Countries::exists($input))
Expand Down
57 changes: 57 additions & 0 deletions src/Rule/Language.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace ProgrammatorDev\Validator\Rule;

use ProgrammatorDev\Validator\Exception\LanguageException;
use ProgrammatorDev\Validator\Exception\UnexpectedOptionException;
use ProgrammatorDev\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Intl\Languages;

class Language extends AbstractRule implements RuleInterface
{
public const ALPHA_2_CODE = 'alpha-2';
public const ALPHA_3_CODE = 'alpha-3';

private const CODE_OPTIONS = [
self::ALPHA_2_CODE,
self::ALPHA_3_CODE
];

private string $message = 'The {{ name }} value is not a valid language, {{ value }} given.';

public function __construct(
private readonly string $code = self::ALPHA_2_CODE,
?string $message = null
)
{
$this->message = $message ?? $this->message;
}

public function assert(mixed $value, ?string $name = null): void
{
if (!\in_array($this->code, self::CODE_OPTIONS)) {
throw new UnexpectedOptionException('code', self::CODE_OPTIONS, $this->code);
}

if (!\is_string($value)) {
throw new UnexpectedTypeException('string', get_debug_type($value));
}

// keep original value for parameters
$input = \strtolower($value);

if (
($this->code === self::ALPHA_2_CODE && !Languages::exists($input))
|| ($this->code === self::ALPHA_3_CODE && !Languages::alpha3CodeExists($input))
) {
throw new LanguageException(
message: $this->message,
parameters: [
'name' => $name,
'value' => $value,
'code' => $this->code
]
);
}
}
}
44 changes: 44 additions & 0 deletions src/Rule/Locale.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace ProgrammatorDev\Validator\Rule;

use ProgrammatorDev\Validator\Exception\LocaleException;
use ProgrammatorDev\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Intl\Locales;

class Locale extends AbstractRule implements RuleInterface
{
private string $message = 'The {{ name }} value is not a valid locale, {{ value }} given.';

public function __construct(
private readonly bool $canonicalize = false,
?string $message = null
)
{
$this->message = $message ?? $this->message;
}

public function assert(mixed $value, ?string $name = null): void
{
if (!\is_string($value)) {
throw new UnexpectedTypeException('string', get_debug_type($value));
}

// keep original value for parameters
$input = $value;

if ($this->canonicalize) {
$input = \Locale::canonicalize($input);
}

if (!Locales::exists($input)) {
throw new LocaleException(
message: $this->message,
parameters: [
'name' => $name,
'value' => $value
]
);
}
}
}
10 changes: 10 additions & 0 deletions src/StaticValidatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ public static function greaterThanOrEqual(
?string $message = null
): ChainedValidatorInterface&Validator;

public static function language(
string $code = 'alpha-2',
?string $message = null
): ChainedValidatorInterface&Validator;

public static function length(
?int $min = null,
?int $max = null,
Expand All @@ -91,6 +96,11 @@ public static function lessThanOrEqual(
?string $message = null
): ChainedValidatorInterface&Validator;

public static function locale(
bool $canonicalize = false,
?string $message = null
): ChainedValidatorInterface&Validator;

public static function notBlank(
?callable $normalizer = null,
?string $message = null
Expand Down
16 changes: 8 additions & 8 deletions tests/ChoiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,39 +66,39 @@ public static function provideRuleMessageOptionData(): \Generator
yield 'message' => [
new Choice(
constraints: $constraints,
message: 'The {{ name }} value {{ value }} is not a valid choice.'
message: '{{ name }} | {{ value }} | {{ constraints }}'
),
10,
'The test value 10 is not a valid choice.'
'test | 10 | [1, 2, 3, 4, 5]'
];
yield 'multiple message' => [
new Choice(
constraints: $constraints,
multiple: true,
multipleMessage: 'The {{ name }} value {{ value }} is not a valid choice.'
multipleMessage: '{{ name }} | {{ value }} | {{ constraints }}'
),
[10],
'The test value [10] is not a valid choice.'
'test | [10] | [1, 2, 3, 4, 5]'
];
yield 'min message' => [
new Choice(
constraints: $constraints,
multiple: true,
min: 2,
minMessage: 'The {{ name }} value should have at least {{ min }} choices.'
minMessage: '{{ name }} | {{ value }} | {{ constraints }} | {{ min }} | {{ max }} | {{ numElements }}'
),
[1],
'The test value should have at least 2 choices.'
'test | [1] | [1, 2, 3, 4, 5] | 2 | null | 1'
];
yield 'max message' => [
new Choice(
constraints: $constraints,
multiple: true,
max: 2,
maxMessage: 'The {{ name }} value should have at most {{ max }} choices.'
maxMessage: '{{ name }} | {{ value }} | {{ constraints }} | {{ min }} | {{ max }} | {{ numElements }}'
),
[1, 2, 3],
'The test value should have at most 2 choices.'
'test | [1, 2, 3] | [1, 2, 3, 4, 5] | null | 2 | 3'
];
}

Expand Down

0 comments on commit e32b2dc

Please sign in to comment.