diff --git a/README.md b/README.md index 85fb275..f0fc14a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ $validator = Validator::type('int')->greaterThanOrEqual(18); // and validate with these: $validator->validate(16); // returns bool: false -$validator->assert(16, 'age'); // throws exception: The age value should be greater than or equal to 18, 16 given. +$validator->assert(16, 'age'); // throws exception: The age value should be greater than or equal to 18. ``` ## Documentation diff --git a/docs/01-get-started.md b/docs/01-get-started.md index 9304c18..3f8433f 100644 --- a/docs/01-get-started.md +++ b/docs/01-get-started.md @@ -28,5 +28,5 @@ $validator = Validator::type('int')->greaterThanOrEqual(18); // and validate with these: $validator->validate(16); // returns bool: false -$validator->assert(16, 'age'); // throws exception: The age value should be greater than or equal to 18, 16 given. +$validator->assert(16, 'age'); // throws exception: The age value should be greater than or equal to 18. ``` \ No newline at end of file diff --git a/docs/03-rules.md b/docs/03-rules.md index 58474c3..79dd7ba 100644 --- a/docs/03-rules.md +++ b/docs/03-rules.md @@ -12,7 +12,11 @@ - [Blank](03-rules_blank.md) - [Count](03-rules_count.md) +- [IsFalse](03-rules_is-false.md) +- [IsNull](03-rules_is-null.md) +- [IsTrue](03-rules_is-true.md) - [NotBlank](03-rules_not-blank.md) +- [NotNull](03-rules_not-null.md) - [Type](03-rules_type.md) ## String Rules @@ -52,4 +56,5 @@ ## Other Rules +- [AtLeastOneOf](03-rules_at-least-one-of.md) - [Optional](03-rules_optional.md) \ No newline at end of file diff --git a/docs/03-rules_at-least-one-of.md b/docs/03-rules_at-least-one-of.md new file mode 100644 index 0000000..9f84ed8 --- /dev/null +++ b/docs/03-rules_at-least-one-of.md @@ -0,0 +1,65 @@ +# AtLeastOneOf + +Checks that the value satisfies at least one of the given constraints. + +```php +/** Validator[] $constraints */ +Choice( + array $constraints, + ?string $message = null +); +``` + +## Basic Usage + +```php +Validator::atLeastOneOf([ + Validator::isFalse(), + Validator::type('int')->greaterThanOrEqual(18) +])->validate(false); // true + +Validator::atLeastOneOf([ + Validator::isFalse(), + Validator::type('int')->greaterThanOrEqual(18) +])->validate(20); // true + +Validator::atLeastOneOf([ + Validator::isFalse(), + Validator::type('int')->greaterThanOrEqual(18) +])->validate(true); // false + +Validator::atLeastOneOf([ + Validator::isFalse(), + Validator::type('int')->greaterThanOrEqual(18) +])->validate(16); // false +``` + +> [!NOTE] +> An `UnexpectedValueException` will be thrown when a value in the `constraints` array is not an instance of `Validator`. + +## Options + +### `constraints` + +type: `array` `required` + +Collection of constraints to be validated against the input value. +If at least one given constraint is valid, the validation is considered successful. + +### `message` + +type: `?string` default: `The {{ name }} value should satisfy at least one of the following constraints: {{ messages }}` + +Message that will be shown if all given constraints are not valid. + +The following parameters are available: + +| Parameter | Description | +|------------------|-------------------------------------------------| +| `{{ value }}` | The current invalid value | +| `{{ name }}` | Name of the invalid value | +| `{{ messages }}` | List of error messages based on the constraints | + +## Changelog + +- `1.3.0` Created \ No newline at end of file diff --git a/docs/03-rules_blank.md b/docs/03-rules_blank.md index 0635b6a..01ba143 100644 --- a/docs/03-rules_blank.md +++ b/docs/03-rules_blank.md @@ -40,7 +40,7 @@ Validator::blank(normalizer: fn($value) => trim($value))->validate(' '); // true ### `message` -type: `?string` default: `The {{ name }} value should be blank, {{ value }} given.` +type: `?string` default: `The {{ name }} value should be blank.` Message that will be shown if the value is not blank. diff --git a/docs/03-rules_choice.md b/docs/03-rules_choice.md index ba1bfd4..25669d8 100644 --- a/docs/03-rules_choice.md +++ b/docs/03-rules_choice.md @@ -42,9 +42,6 @@ Validator::choice(['red', 'green', 'blue'], multiple: true, min: 2, max: 3)->val > [!NOTE] > An `UnexpectedValueException` will be thrown when `multiple` is `true` and the input value is not an `array`. -> [!NOTE] -> An `UnexpectedValueException` will be thrown when the `min` value is greater than or equal to the `max` value. - ## Options ### `constraints` @@ -78,7 +75,7 @@ For example, if `max` is 2, the input array must have at most 2 values. ### `message` -type: `?string` default: `The {{ name }} value is not a valid choice, {{ value }} given. Accepted values are: {{ constraints }}.` +type: `?string` default: `The {{ name }} value is not a valid choice. Accepted values are: {{ constraints }}.` Message that will be shown if input value is not a valid choice. @@ -92,7 +89,7 @@ The following parameters are available: ### `multipleMessage` -type: `?string` default: `The {{ name }} value has one or more invalid choices, {{ value }} given. Accepted values are: {{ constraints }}.` +type: `?string` default: `The {{ name }} value has one or more invalid choices. Accepted values are: {{ constraints }}.` Message that will be shown when `multiple` is `true` and at least one of the input array values is not a valid choice. @@ -106,7 +103,7 @@ The following parameters are available: ### `minMessage` -type: `?string` default: `The {{ name }} value must have at least {{ min }} choices, {{ numElements }} choices given.` +type: `?string` default: `The {{ name }} value must have at least {{ min }} choice(s).` Message that will be shown when `multiple` is `true` and input array has fewer values than the defined in `min`. @@ -123,7 +120,7 @@ The following parameters are available: ### `maxMessage` -type: `?string` default: `The {{ name }} value must have at most {{ max }} choices, {{ numElements }} choices given.` +type: `?string` default: `The {{ name }} value must have at most {{ max }} choice(s).` Message that will be shown when `multiple` is `true` and input array has more values than the defined in `max`. diff --git a/docs/03-rules_count.md b/docs/03-rules_count.md index 138ce6a..f33e458 100644 --- a/docs/03-rules_count.md +++ b/docs/03-rules_count.md @@ -32,9 +32,6 @@ Validator::count(min: 3, max: 3)->validate(['a', 'b', 'c']); // true > [!NOTE] > An `UnexpectedValueException` will be thrown when either `min` or `max` options are not given. -> [!NOTE] -> An `UnexpectedValueException` will be thrown when the `min` value is greater than the `max` value. - > [!NOTE] > An `UnexpectedValueException` will be thrown when the input value is not an `array` or an object implementing `\Countable`. @@ -54,7 +51,7 @@ It defines the maximum number of elements required. ### `minMessage` -type: `?string` default: `The {{ name }} value should contain {{ min }} elements or more, {{ numElements }} elements given.` +type: `?string` default: `The {{ name }} value should contain {{ min }} elements or more.` Message that will be shown when the input value has fewer elements than the defined in `min`. @@ -70,7 +67,7 @@ The following parameters are available: ### `maxMessage` -type: `?string` default: `The {{ name }} value should contain {{ max }} elements or less, {{ numElements }} elements given.` +type: `?string` default: `The {{ name }} value should contain {{ max }} elements or less.` Message that will be shown when the input value has more elements than the defined in `max`. @@ -86,7 +83,7 @@ The following parameters are available: ### `exactMessage` -type: `?string` default: `The {{ name }} value should contain exactly {{ min }} elements, {{ numElements }} elements given.` +type: `?string` default: `The {{ name }} value should contain exactly {{ min }} elements.` Message that will be shown when `min` and `max` options have the same value and the input value has a different number of elements. diff --git a/docs/03-rules_country.md b/docs/03-rules_country.md index 3b83684..f9cf53c 100644 --- a/docs/03-rules_country.md +++ b/docs/03-rules_country.md @@ -41,7 +41,7 @@ Available options: ### `message` -type: `?string` default: `The {{ name }} value is not a valid country, {{ value }} given.` +type: `?string` default: `The {{ name }} value is not a valid country.` Message that will be shown if the input value is not a valid country code. diff --git a/docs/03-rules_each-key.md b/docs/03-rules_each-key.md index 525979b..be150f8 100644 --- a/docs/03-rules_each-key.md +++ b/docs/03-rules_each-key.md @@ -34,12 +34,12 @@ Validator that will validate each key of an `array` or object implementing `\Tra ### `message` -type: `?string` default: `Invalid key: {{ message }}` +type: `?string` default: `Invalid key {{ key }}: {{ message }}` Message that will be shown if at least one input value key is invalid according to the given `validator`. ```php -// Throws: Invalid key: The color key value should be of type "string", 1 given. +// throws: Invalid key 1: The value should be of type "string". Validator::eachKey( Validator::type('string') )->assert(['red' => '#f00', 1 => '#0f0'], 'color'); diff --git a/docs/03-rules_each-value.md b/docs/03-rules_each-value.md index a625382..f12242a 100644 --- a/docs/03-rules_each-value.md +++ b/docs/03-rules_each-value.md @@ -39,7 +39,7 @@ type: `?string` default: `At key "{{ key }}": {{ message }}` Message that will be shown if at least one input value element is invalid according to the given `validator`. ```php -// Throws: At key 2: The color value should not be blank, "" given. +// throws: At key 2: The color value should not be blank. Validator::eachValue( Validator::notBlank() )->assert(['red', 'green', ''], 'color'); diff --git a/docs/03-rules_email.md b/docs/03-rules_email.md index e0c27f8..96d9536 100644 --- a/docs/03-rules_email.md +++ b/docs/03-rules_email.md @@ -56,7 +56,7 @@ Validator::email(normalizer: fn($value) => trim($value))->validate('test@example ### `message` -type: `?string` default: `The {{ name }} value is not a valid email address, {{ value }} given.` +type: `?string` default: `The {{ name }} value is not a valid email address.` Message that will be shown if the input value is not a valid email address. diff --git a/docs/03-rules_greater-than-or-equal.md b/docs/03-rules_greater-than-or-equal.md index 9a12eaf..6ea2573 100644 --- a/docs/03-rules_greater-than-or-equal.md +++ b/docs/03-rules_greater-than-or-equal.md @@ -44,7 +44,7 @@ Can be a `string`, `int`, `float` or `DateTimeInterface` object. ### `message` -type: `?string` default: `The {{ name }} value should be greater than or equal to {{ constraint }}, {{ value }} given.` +type: `?string` default: `The {{ name }} value should be greater than or equal to {{ constraint }}.` Message that will be shown if the value is not greater than or equal to the constraint value. diff --git a/docs/03-rules_greater-than.md b/docs/03-rules_greater-than.md index e3a7175..9f09569 100644 --- a/docs/03-rules_greater-than.md +++ b/docs/03-rules_greater-than.md @@ -44,7 +44,7 @@ Can be a `string`, `int`, `float` or `DateTimeInterface` object. ### `message` -type: `?string` default: `The {{ name }} value should be greater than {{ constraint }}, {{ value }} given.` +type: `?string` default: `The {{ name }} value should be greater than {{ constraint }}.` Message that will be shown if the value is not greater than the constraint value. diff --git a/docs/03-rules_is-false.md b/docs/03-rules_is-false.md new file mode 100644 index 0000000..faf7ccc --- /dev/null +++ b/docs/03-rules_is-false.md @@ -0,0 +1,37 @@ +# IsFalse + +Validates that a value is `false`. + +Check the [IsTrue](03-rules_is-true.md) rule for a `true` validation. + +```php +IsFalse( + ?string $message = null +); +``` + +## Basic Usage + +```php +// anything else will be false +Validator::isFalse()->validate(false); // true +``` + +## Options + +### `message` + +type: `?string` default: `The {{ name }} value should be false.` + +Message that will be shown if the value is false. + +The following parameters are available: + +| Parameter | Description | +|---------------|---------------------------| +| `{{ value }}` | The current invalid value | +| `{{ name }}` | Name of the invalid value | + +## Changelog + +- `1.3.0` Created \ No newline at end of file diff --git a/docs/03-rules_is-null.md b/docs/03-rules_is-null.md new file mode 100644 index 0000000..966cdbf --- /dev/null +++ b/docs/03-rules_is-null.md @@ -0,0 +1,37 @@ +# IsNull + +Validates that a value is `null`. + +Check the [NotNull](03-rules_not-null.md) rule for the opposite validation. + +```php +IsNull( + ?string $message = null +); +``` + +## Basic Usage + +```php +// anything else will be false +Validator::isNull()->validate(null); // true +``` + +## Options + +### `message` + +type: `?string` default: `The {{ name }} value should be null.` + +Message that will be shown if the value is null. + +The following parameters are available: + +| Parameter | Description | +|---------------|---------------------------| +| `{{ value }}` | The current invalid value | +| `{{ name }}` | Name of the invalid value | + +## Changelog + +- `1.3.0` Created \ No newline at end of file diff --git a/docs/03-rules_is-true.md b/docs/03-rules_is-true.md new file mode 100644 index 0000000..72244fc --- /dev/null +++ b/docs/03-rules_is-true.md @@ -0,0 +1,37 @@ +# IsTrue + +Validates that a value is `true`. + +Check the [IsFalse](03-rules_is-false.md) rule for a `false` validation. + +```php +IsTrue( + ?string $message = null +); +``` + +## Basic Usage + +```php +// anything else will be false +Validator::isTrue()->validate(true); // true +``` + +## Options + +### `message` + +type: `?string` default: `The {{ name }} value should be true.` + +Message that will be shown if the value is true. + +The following parameters are available: + +| Parameter | Description | +|---------------|---------------------------| +| `{{ value }}` | The current invalid value | +| `{{ name }}` | Name of the invalid value | + +## Changelog + +- `1.3.0` Created \ No newline at end of file diff --git a/docs/03-rules_language.md b/docs/03-rules_language.md index f50427d..8eda5e3 100644 --- a/docs/03-rules_language.md +++ b/docs/03-rules_language.md @@ -41,7 +41,7 @@ Available options: ### `message` -type: `?string` default: `The {{ name }} value is not a valid language, {{ value }} given.` +type: `?string` default: `The {{ name }} value is not a valid language.` Message that will be shown if the input value is not a valid language code. diff --git a/docs/03-rules_length.md b/docs/03-rules_length.md index aea03de..0f19612 100644 --- a/docs/03-rules_length.md +++ b/docs/03-rules_length.md @@ -43,9 +43,6 @@ Validator::length(max: 1, countUnit: 'graphemes')->validate('🔥'); // true > [!NOTE] > An `UnexpectedValueException` will be thrown when either `min` or `max` options are not given. -> [!NOTE] -> An `UnexpectedValueException` will be thrown when the `min` value is greater than the `max` value. - > [!NOTE] > An `UnexpectedValueException` will be thrown when the `charset` value is not a valid option. > Check all the supported character encodings [here](https://www.php.net/manual/en/mbstring.supported-encodings.php). @@ -107,7 +104,7 @@ Validator::length(max: 3, normalizer: fn($value) => trim($value))->validate('abc ### `minMessage` -type: `?string` default: `The {{ name }} value should have {{ min }} characters or more, {{ numChars }} characters given.` +type: `?string` default: `The {{ name }} value should have {{ min }} character(s) or more.` Message that will be shown when the input value has fewer characters than the defined in `min`. @@ -125,7 +122,7 @@ The following parameters are available: ### `maxMessage` -type: `?string` default: `The {{ name }} value should have {{ max }} characters or less, {{ numChars }} characters given.` +type: `?string` default: `The {{ name }} value should have {{ max }} character(s) or less.` Message that will be shown when the input value has more characters than the defined in `max`. @@ -143,7 +140,7 @@ The following parameters are available: ### `exactMessage` -type: `?string` default: `The {{ name }} value should have exactly {{ min }} characters, {{ numChars }} characters given.` +type: `?string` default: `The {{ name }} value should have exactly {{ min }} characters.` Message that will be shown when `min` and `max` options have the same value and the input value has a different number of characters. diff --git a/docs/03-rules_less-than-or-equal.md b/docs/03-rules_less-than-or-equal.md index ebcdfc4..2aa827e 100644 --- a/docs/03-rules_less-than-or-equal.md +++ b/docs/03-rules_less-than-or-equal.md @@ -44,7 +44,7 @@ Can be a `string`, `int`, `float` or `DateTimeInterface` object. ### `message` -type: `?string` default: `The {{ name }} value should be less than or equal to {{ constraint }}, {{ value }} given.` +type: `?string` default: `The {{ name }} value should be less than or equal to {{ constraint }}.` Message that will be shown if the value is not less than or equal to the constraint value. diff --git a/docs/03-rules_less-than.md b/docs/03-rules_less-than.md index 0343296..21298bd 100644 --- a/docs/03-rules_less-than.md +++ b/docs/03-rules_less-than.md @@ -44,7 +44,7 @@ Can be a `string`, `int`, `float` or `DateTimeInterface` object. ### `message` -type: `?string` default: `The {{ name }} value should be less than {{ constraint }}, {{ value }} given.` +type: `?string` default: `The {{ name }} value should be less than {{ constraint }}.` Message that will be shown if the value is not less than the constraint value. diff --git a/docs/03-rules_locale.md b/docs/03-rules_locale.md index 6c76743..f6e7f77 100644 --- a/docs/03-rules_locale.md +++ b/docs/03-rules_locale.md @@ -40,7 +40,7 @@ If `true`, the input value will be normalized before validation, according to th ### `message` -type: `?string` default: `The {{ name }} value is not a valid locale, {{ value }} given.` +type: `?string` default: `The {{ name }} value is not a valid locale.` Message that will be shown if the input value is not a valid locale code. diff --git a/docs/03-rules_not-blank.md b/docs/03-rules_not-blank.md index 9278282..f53b88f 100644 --- a/docs/03-rules_not-blank.md +++ b/docs/03-rules_not-blank.md @@ -40,7 +40,7 @@ Validator::notBlank(normalizer: fn($value) => trim($value))->validate(' '); // f ### `message` -type: `?string` default: `The {{ name }} value should not be blank, {{ value }} given.` +type: `?string` default: `The {{ name }} value should not be blank.` Message that will be shown if the value is blank. diff --git a/docs/03-rules_not-null.md b/docs/03-rules_not-null.md new file mode 100644 index 0000000..b249142 --- /dev/null +++ b/docs/03-rules_not-null.md @@ -0,0 +1,36 @@ +# NotNull + +Validates that a value is not `null`. + +Check the [IsNull](03-rules_is-null.md) rule for the opposite validation. + +```php +NotNull( + ?string $message = null +); +``` + +## Basic Usage + +```php +// anything else will be true +Validator::notNull()->validate(null); // false +``` + +## Options + +### `message` + +type: `?string` default: `The {{ name }} value should not be null.` + +Message that will be shown if the value is not null. + +The following parameters are available: + +| Parameter | Description | +|---------------|---------------------------| +| `{{ name }}` | Name of the invalid value | + +## Changelog + +- `1.3.0` Created \ No newline at end of file diff --git a/docs/03-rules_range.md b/docs/03-rules_range.md index 98ac45a..4395bec 100644 --- a/docs/03-rules_range.md +++ b/docs/03-rules_range.md @@ -36,9 +36,6 @@ Validator::range(new DateTime('yesterday'), new DateTime('tomorrow'))->validate( > [!NOTE] > An `UnexpectedValueException` will be thrown when trying to compare incomparable values, like a `string` with an `int`. -> [!NOTE] -> An `UnexpectedValueException` will be thrown when the `min` value is greater than or equal to the `max` value. - ## Options ### `min` @@ -57,7 +54,7 @@ Can be a `string`, `int`, `float` or `DateTimeInterface` object. ### `message` -type: `?string` default: `The {{ name }} value should be between {{ min }} and {{ max }}, {{ value }} given.` +type: `?string` default: `The {{ name }} value should be between {{ min }} and {{ max }}.` Message that will be shown if the value is not between the minimum and maximum values. diff --git a/docs/03-rules_timezone.md b/docs/03-rules_timezone.md index 157f524..58d0bf8 100644 --- a/docs/03-rules_timezone.md +++ b/docs/03-rules_timezone.md @@ -75,7 +75,7 @@ Check the [official country codes](https://en.wikipedia.org/wiki/ISO_3166-1#Curr ### `message` -type: `?string` default: `The {{ name }} value is not a valid timezone, {{ value }} given.` +type: `?string` default: `The {{ name }} value is not a valid timezone.` Message that will be shown if the input value is not a valid timezone. diff --git a/docs/03-rules_type.md b/docs/03-rules_type.md index 1f8df29..34279eb 100644 --- a/docs/03-rules_type.md +++ b/docs/03-rules_type.md @@ -77,7 +77,7 @@ Available character type constraints: ### `message` -type: `?string` default: `The {{ name }} value should be of type {{ constraint }}, {{ value }} given.` +type: `?string` default: `The {{ name }} value should be of type {{ constraint }}.` Message that will be shown if input value is not of a specific type. diff --git a/docs/03-rules_url.md b/docs/03-rules_url.md index 231c958..d136117 100644 --- a/docs/03-rules_url.md +++ b/docs/03-rules_url.md @@ -58,7 +58,7 @@ Validator::url(normalizer: fn($value) => trim($value))->validate('https://exampl ### `message` -type: `?string` default: `The {{ name }} value is not a valid URL address, {{ value }} given.` +type: `?string` default: `The {{ name }} value is not a valid URL address.` Message that will be shown if the input value is not a valid URL address. diff --git a/src/ChainedValidatorInterface.php b/src/ChainedValidatorInterface.php index c7b286b..8959be9 100644 --- a/src/ChainedValidatorInterface.php +++ b/src/ChainedValidatorInterface.php @@ -7,6 +7,11 @@ interface ChainedValidatorInterface { + public function atLeastOneOf( + array $constraints, + ?string $message = null + ): ChainedValidatorInterface&Validator; + public function blank( ?callable $normalizer = null, ?string $message = null @@ -80,6 +85,18 @@ public function greaterThanOrEqual( ?string $message = null ): ChainedValidatorInterface&Validator; + public function isFalse( + ?string $message = null + ): ChainedValidatorInterface&Validator; + + public function isNull( + ?string $message = null + ): ChainedValidatorInterface&Validator; + + public function isTrue( + ?string $message = null + ): ChainedValidatorInterface&Validator; + public function language( string $code = 'alpha-2', ?string $message = null @@ -117,6 +134,10 @@ public function notBlank( ?string $message = null ): ChainedValidatorInterface&Validator; + public function notNull( + ?string $message = null + ): ChainedValidatorInterface&Validator; + public function optional( Validator $validator ): ChainedValidatorInterface&Validator; diff --git a/src/Exception/AtLeastOneOfException.php b/src/Exception/AtLeastOneOfException.php new file mode 100644 index 0000000..899db18 --- /dev/null +++ b/src/Exception/AtLeastOneOfException.php @@ -0,0 +1,5 @@ + $value) { + // format values (with some exceptions to avoid adding unnecessary quotation marks) + $message = \str_replace( + \sprintf('{{ %s }}', $parameter), + (\in_array($parameter, ['name', 'message', 'messages'])) ? $value : $this->formatValue($value), + $message + ); + } + + return $message; + } + + private function formatValue(mixed $value): string + { + if ($value instanceof \DateTimeInterface) { + return $value->format('Y-m-d H:i:s'); + } + + if (\is_object($value)) { + if ($value instanceof \Stringable) { + return $value->__toString(); + } + + return 'object'; + } + + if (\is_array($value)) { + return $this->formatValues($value); + } + + if (\is_string($value)) { + // replace line breaks and tabs with single space + $value = \str_replace(["\n", "\r", "\t", "\v", "\x00"], ' ', $value); + + return \sprintf('"%s"', $value); + } + + if (\is_resource($value)) { + return 'resource'; + } + + if ($value === null) { + return 'null'; + } + + if ($value === false) { + return 'false'; + } + + if ($value === true) { + return 'true'; + } + + return (string) $value; + } + + private function formatValues(array $values): string + { + foreach ($values as $key => $value) { + $values[$key] = $this->formatValue($value); + } + + return \sprintf('[%s]', \implode(', ', $values)); + } +} \ No newline at end of file diff --git a/src/Exception/ValidationException.php b/src/Exception/ValidationException.php index 7184434..2acb64b 100644 --- a/src/Exception/ValidationException.php +++ b/src/Exception/ValidationException.php @@ -2,85 +2,16 @@ namespace ProgrammatorDev\Validator\Exception; +use ProgrammatorDev\Validator\Exception\Util\MessageTrait; + class ValidationException extends \Exception { + use MessageTrait; + public function __construct(string $message, array $parameters = []) { $message = $this->formatMessage($message, $parameters); parent::__construct($message); } - - private function formatMessage(string $message, array $parameters = []): string - { - // If a name was not given, remove it from the message template but keep it intuitive - if (empty($parameters['name'])) { - $message = \str_replace(' {{ name }} ', ' ', $message); - unset($parameters['name']); - } - - foreach ($parameters as $parameter => $value) { - // Format values (with some exceptions [name, message] to avoid adding unnecessary quotation marks) - $message = \str_replace( - \sprintf('{{ %s }}', $parameter), - (\in_array($parameter, ['name', 'message'])) ? $value : $this->formatValue($value), - $message - ); - } - - return $message; - } - - private function formatValue(mixed $value): string - { - if ($value instanceof \DateTimeInterface) { - return $value->format('Y-m-d H:i:s'); - } - - if (\is_object($value)) { - if ($value instanceof \Stringable) { - return $value->__toString(); - } - - return 'object'; - } - - if (\is_array($value)) { - return $this->formatValues($value); - } - - if (\is_string($value)) { - // Replace line breaks and tabs with single space - $value = \str_replace(["\n", "\r", "\t", "\v", "\x00"], ' ', $value); - - return \sprintf('"%s"', $value); - } - - if (\is_resource($value)) { - return 'resource'; - } - - if ($value === null) { - return 'null'; - } - - if ($value === false) { - return 'false'; - } - - if ($value === true) { - return 'true'; - } - - return (string) $value; - } - - private function formatValues(array $values): string - { - foreach ($values as $key => $value) { - $values[$key] = $this->formatValue($value); - } - - return \sprintf('[%s]', \implode(', ', $values)); - } } \ No newline at end of file diff --git a/src/Rule/AbstractComparisonRule.php b/src/Rule/AbstractComparisonRule.php index bbe9c92..0056989 100644 --- a/src/Rule/AbstractComparisonRule.php +++ b/src/Rule/AbstractComparisonRule.php @@ -12,10 +12,7 @@ abstract class AbstractComparisonRule extends AbstractRule public function assert(mixed $value, ?string $name = null): void { if (!$this->isComparable($value, $this->constraint)) { - throw new UnexpectedComparableException( - get_debug_type($value), - get_debug_type($this->constraint) - ); + throw new UnexpectedComparableException($value, $this->constraint); } if (!$this->compareValues($value, $this->constraint)) { diff --git a/src/Rule/AtLeastOneOf.php b/src/Rule/AtLeastOneOf.php new file mode 100644 index 0000000..7d0fe69 --- /dev/null +++ b/src/Rule/AtLeastOneOf.php @@ -0,0 +1,59 @@ +message = $message ?? $this->message; + } + + public function assert(mixed $value, ?string $name = null): void + { + try { + Validator::eachValue( + validator: Validator::type(Validator::class) + )->assert($this->constraints); + } + catch (ValidationException) { + throw new InvalidOptionException( + name: 'constraints', + expected: \sprintf('All values should be of type "%s".', Validator::class) + ); + } + + $messages = []; + + foreach ($this->constraints as $key => $constraint) { + try { + $constraint->assert($value); + return; + } + catch (ValidationException|UnexpectedValueException $exception) { + $messages[] = \sprintf('[%d] %s', ($key + 1), $exception->getMessage()); + } + } + + throw new AtLeastOneOfException( + message: $this->message, + parameters: [ + 'value' => $value, + 'name' => $name, + 'messages' => \implode(' ', $messages) + ] + ); + } +} \ No newline at end of file diff --git a/src/Rule/Blank.php b/src/Rule/Blank.php index 232c58a..b01a52b 100644 --- a/src/Rule/Blank.php +++ b/src/Rule/Blank.php @@ -8,7 +8,7 @@ class Blank extends AbstractRule implements RuleInterface { /** @var ?callable */ private $normalizer; - private string $message = 'The {{ name }} value should be blank, {{ value }} given.'; + private string $message = 'The {{ name }} value should be blank.'; public function __construct( ?callable $normalizer = null, diff --git a/src/Rule/Choice.php b/src/Rule/Choice.php index 3543c96..c187edc 100644 --- a/src/Rule/Choice.php +++ b/src/Rule/Choice.php @@ -4,14 +4,13 @@ use ProgrammatorDev\Validator\Exception\ChoiceException; use ProgrammatorDev\Validator\Exception\UnexpectedTypeException; -use ProgrammatorDev\Validator\Exception\UnexpectedValueException; class Choice extends AbstractRule implements RuleInterface { - private string $message = 'The {{ name }} value is not a valid choice, {{ value }} given. Accepted values are: {{ constraints }}.'; - private string $multipleMessage = 'The {{ name }} value has one or more invalid choices, {{ value }} given. Accepted values are: {{ constraints }}.'; - private string $minMessage = 'The {{ name }} value must have at least {{ min }} choices, {{ numElements }} choices given.'; - private string $maxMessage = 'The {{ name }} value must have at most {{ max }} choices, {{ numElements }} choices given.'; + private string $message = 'The {{ name }} value is not a valid choice. Accepted values are: {{ constraints }}.'; + private string $multipleMessage = 'The {{ name }} value has one or more invalid choices. Accepted values are: {{ constraints }}.'; + private string $minMessage = 'The {{ name }} value must have at least {{ min }} choice(s).'; + private string $maxMessage = 'The {{ name }} value must have at most {{ max }} choice(s).'; public function __construct( private readonly array $constraints, @@ -33,18 +32,7 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if ($this->multiple && !\is_array($value)) { - throw new UnexpectedTypeException('array', get_debug_type($value)); - } - - if ( - $this->multiple - && $this->min !== null - && $this->max !== null - && $this->min > $this->max - ) { - throw new UnexpectedValueException( - 'Maximum value must be greater than or equal to minimum value.' - ); + throw new UnexpectedTypeException($value, 'array'); } if ($this->multiple) { diff --git a/src/Rule/Collection.php b/src/Rule/Collection.php index b302b90..cc6f6f5 100644 --- a/src/Rule/Collection.php +++ b/src/Rule/Collection.php @@ -3,8 +3,8 @@ namespace ProgrammatorDev\Validator\Rule; use ProgrammatorDev\Validator\Exception\CollectionException; +use ProgrammatorDev\Validator\Exception\InvalidOptionException; use ProgrammatorDev\Validator\Exception\UnexpectedTypeException; -use ProgrammatorDev\Validator\Exception\UnexpectedValueException; use ProgrammatorDev\Validator\Exception\ValidationException; use ProgrammatorDev\Validator\Validator; @@ -33,15 +33,17 @@ public function assert(mixed $value, ?string $name = null): void try { Validator::eachValue( validator: Validator::type(Validator::class), - message: 'At field {{ key }}: {{ message }}' )->assert($this->fields); } - catch (ValidationException $exception) { - throw new UnexpectedValueException($exception->getMessage()); + catch (ValidationException) { + throw new InvalidOptionException( + name: 'fields', + expected: \sprintf('All values should be of type "%s".', Validator::class) + ); } if (!\is_iterable($value)) { - throw new UnexpectedTypeException('array|\Traversable', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'array|\Traversable'); } foreach ($this->fields as $field => $validator) { diff --git a/src/Rule/Count.php b/src/Rule/Count.php index 595b346..d5ab862 100644 --- a/src/Rule/Count.php +++ b/src/Rule/Count.php @@ -3,15 +3,14 @@ namespace ProgrammatorDev\Validator\Rule; use ProgrammatorDev\Validator\Exception\CountException; +use ProgrammatorDev\Validator\Exception\OptionDefinitionException; use ProgrammatorDev\Validator\Exception\UnexpectedTypeException; -use ProgrammatorDev\Validator\Exception\UnexpectedValueException; -use ProgrammatorDev\Validator\Validator; class Count extends AbstractRule implements RuleInterface { - private string $minMessage = 'The {{ name }} value should contain {{ min }} elements or more, {{ numElements }} elements given.'; - private string $maxMessage = 'The {{ name }} value should contain {{ max }} elements or less, {{ numElements }} elements given.'; - private string $exactMessage = 'The {{ name }} value should contain exactly {{ min }} elements, {{ numElements }} elements given.'; + private string $minMessage = 'The {{ name }} value should contain {{ min }} elements or more.'; + private string $maxMessage = 'The {{ name }} value should contain {{ max }} elements or less.'; + private string $exactMessage = 'The {{ name }} value should contain exactly {{ min }} elements.'; public function __construct( private readonly ?int $min = null, @@ -29,19 +28,11 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if ($this->min === null && $this->max === null) { - throw new UnexpectedValueException('At least one of the options "min" or "max" must be given.'); - } - - if ( - $this->min !== null - && $this->max !== null - && !Validator::greaterThanOrEqual($this->min)->validate($this->max) - ) { - throw new UnexpectedValueException('Maximum value must be greater than or equal to minimum value.'); + throw new OptionDefinitionException('At least one of the "min" or "max" options must be specified.'); } if (!\is_countable($value)) { - throw new UnexpectedTypeException('array|\Countable', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'array|\Countable'); } $numElements = \count($value); diff --git a/src/Rule/Country.php b/src/Rule/Country.php index 657cc9f..3d86551 100644 --- a/src/Rule/Country.php +++ b/src/Rule/Country.php @@ -3,7 +3,7 @@ namespace ProgrammatorDev\Validator\Rule; use ProgrammatorDev\Validator\Exception\CountryException; -use ProgrammatorDev\Validator\Exception\UnexpectedOptionException; +use ProgrammatorDev\Validator\Exception\InvalidOptionException; use ProgrammatorDev\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Intl\Countries; @@ -17,7 +17,7 @@ class Country extends AbstractRule implements RuleInterface self::ALPHA_3_CODE ]; - private string $message = 'The {{ name }} value is not a valid country, {{ value }} given.'; + private string $message = 'The {{ name }} value is not a valid country.'; public function __construct( private readonly string $code = self::ALPHA_2_CODE, @@ -30,11 +30,11 @@ public function __construct( 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); + throw new InvalidOptionException('code', self::CODE_OPTIONS); } if (!\is_string($value)) { - throw new UnexpectedTypeException('string', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'string'); } // keep original value for parameters diff --git a/src/Rule/CssColor.php b/src/Rule/CssColor.php index ac2e1bf..130bd48 100644 --- a/src/Rule/CssColor.php +++ b/src/Rule/CssColor.php @@ -3,7 +3,7 @@ namespace ProgrammatorDev\Validator\Rule; use ProgrammatorDev\Validator\Exception\CssColorException; -use ProgrammatorDev\Validator\Exception\UnexpectedOptionException; +use ProgrammatorDev\Validator\Exception\InvalidOptionException; use ProgrammatorDev\Validator\Exception\UnexpectedTypeException; class CssColor extends AbstractRule implements RuleInterface @@ -85,12 +85,12 @@ public function assert(mixed $value, ?string $name = null): void { foreach ($this->formats as $format) { if (!\in_array($format, self::COLOR_FORMATS, true)) { - throw new UnexpectedOptionException('format', self::COLOR_FORMATS, $format); + throw new InvalidOptionException('formats', self::COLOR_FORMATS); } } if (!\is_string($value)) { - throw new UnexpectedTypeException('string', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'string'); } foreach ($this->formats as $format) { diff --git a/src/Rule/DateTime.php b/src/Rule/DateTime.php index c347e1a..0cdaef4 100644 --- a/src/Rule/DateTime.php +++ b/src/Rule/DateTime.php @@ -20,7 +20,7 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if (!\is_scalar($value) && !$value instanceof \Stringable) { - throw new UnexpectedTypeException('string|\Stringable', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'string|\Stringable'); } $value = (string) $value; diff --git a/src/Rule/EachKey.php b/src/Rule/EachKey.php index c7f2e4d..5a202d9 100644 --- a/src/Rule/EachKey.php +++ b/src/Rule/EachKey.php @@ -9,7 +9,7 @@ class EachKey extends AbstractRule implements RuleInterface { - private string $message = 'Invalid key: {{ message }}'; + private string $message = 'Invalid key {{ key }}: {{ message }}'; public function __construct( private readonly Validator $validator, @@ -22,12 +22,12 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if (!\is_iterable($value)) { - throw new UnexpectedTypeException('array|\Traversable', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'array|\Traversable'); } foreach ($value as $key => $element) { try { - $this->validator->assert($key, $name); + $this->validator->assert($key); } catch (ValidationException $exception) { throw new EachKeyException( @@ -37,8 +37,7 @@ public function assert(mixed $value, ?string $name = null): void 'name' => $name, 'key' => $key, 'element' => $element, - // Replaces string "value" with string "key value" to get a more intuitive error message - 'message' => \str_replace(' value ', ' key value ', $exception->getMessage()) + 'message' => $exception->getMessage() ] ); } diff --git a/src/Rule/EachValue.php b/src/Rule/EachValue.php index ece714f..10b0bc3 100644 --- a/src/Rule/EachValue.php +++ b/src/Rule/EachValue.php @@ -22,7 +22,7 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if (!\is_iterable($value)) { - throw new UnexpectedTypeException('array|\Traversable', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'array|\Traversable'); } foreach ($value as $key => $element) { diff --git a/src/Rule/Email.php b/src/Rule/Email.php index ac7548d..e6f164b 100644 --- a/src/Rule/Email.php +++ b/src/Rule/Email.php @@ -5,7 +5,7 @@ use Egulias\EmailValidator\EmailValidator; use Egulias\EmailValidator\Validation\NoRFCWarningsValidation; use ProgrammatorDev\Validator\Exception\EmailException; -use ProgrammatorDev\Validator\Exception\UnexpectedOptionException; +use ProgrammatorDev\Validator\Exception\InvalidOptionException; use ProgrammatorDev\Validator\Exception\UnexpectedTypeException; class Email extends AbstractRule implements RuleInterface @@ -27,7 +27,7 @@ class Email extends AbstractRule implements RuleInterface /** @var ?callable */ private $normalizer; - private string $message = 'The {{ name }} value is not a valid email address, {{ value }} given.'; + private string $message = 'The {{ name }} value is not a valid email address.'; public function __construct( private readonly string $mode = self::MODE_HTML5, @@ -42,11 +42,11 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if (!\in_array($this->mode, self::EMAIL_MODES, true)) { - throw new UnexpectedOptionException('mode', self::EMAIL_MODES, $this->mode); + throw new InvalidOptionException('mode', self::EMAIL_MODES); } if (!\is_string($value)) { - throw new UnexpectedTypeException('string', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'string'); } if ($this->normalizer !== null) { diff --git a/src/Rule/GreaterThan.php b/src/Rule/GreaterThan.php index dbefcd6..c302e9a 100644 --- a/src/Rule/GreaterThan.php +++ b/src/Rule/GreaterThan.php @@ -6,7 +6,7 @@ class GreaterThan extends AbstractComparisonRule implements RuleInterface { - protected string $message = 'The {{ name }} value should be greater than {{ constraint }}, {{ value }} given.'; + protected string $message = 'The {{ name }} value should be greater than {{ constraint }}.'; public function __construct( protected readonly mixed $constraint, diff --git a/src/Rule/GreaterThanOrEqual.php b/src/Rule/GreaterThanOrEqual.php index fc52256..f959662 100644 --- a/src/Rule/GreaterThanOrEqual.php +++ b/src/Rule/GreaterThanOrEqual.php @@ -6,7 +6,7 @@ class GreaterThanOrEqual extends AbstractComparisonRule implements RuleInterface { - protected string $message = 'The {{ name }} value should be greater than or equal to {{ constraint }}, {{ value }} given.'; + protected string $message = 'The {{ name }} value should be greater than or equal to {{ constraint }}.'; public function __construct( protected readonly mixed $constraint, diff --git a/src/Rule/IsFalse.php b/src/Rule/IsFalse.php new file mode 100644 index 0000000..e41cd3f --- /dev/null +++ b/src/Rule/IsFalse.php @@ -0,0 +1,30 @@ +message = $message ?? $this->message; + } + + public function assert(mixed $value, ?string $name = null): void + { + if ($value !== false) { + throw new IsFalseException( + message: $this->message, + parameters: [ + 'value' => $value, + 'name' => $name + ] + ); + } + } +} \ No newline at end of file diff --git a/src/Rule/IsNull.php b/src/Rule/IsNull.php new file mode 100644 index 0000000..4cd155f --- /dev/null +++ b/src/Rule/IsNull.php @@ -0,0 +1,30 @@ +message = $message ?? $this->message; + } + + public function assert(mixed $value, ?string $name = null): void + { + if (!\is_null($value)) { + throw new IsNullException( + message: $this->message, + parameters: [ + 'value' => $value, + 'name' => $name + ] + ); + } + } +} \ No newline at end of file diff --git a/src/Rule/IsTrue.php b/src/Rule/IsTrue.php new file mode 100644 index 0000000..6bc1304 --- /dev/null +++ b/src/Rule/IsTrue.php @@ -0,0 +1,30 @@ +message = $message ?? $this->message; + } + + public function assert(mixed $value, ?string $name = null): void + { + if ($value !== true) { + throw new IsTrueException( + message: $this->message, + parameters: [ + 'value' => $value, + 'name' => $name + ] + ); + } + } +} \ No newline at end of file diff --git a/src/Rule/Language.php b/src/Rule/Language.php index 1e711c1..b47de57 100644 --- a/src/Rule/Language.php +++ b/src/Rule/Language.php @@ -3,7 +3,7 @@ namespace ProgrammatorDev\Validator\Rule; use ProgrammatorDev\Validator\Exception\LanguageException; -use ProgrammatorDev\Validator\Exception\UnexpectedOptionException; +use ProgrammatorDev\Validator\Exception\InvalidOptionException; use ProgrammatorDev\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Intl\Languages; @@ -17,7 +17,7 @@ class Language extends AbstractRule implements RuleInterface self::ALPHA_3_CODE ]; - private string $message = 'The {{ name }} value is not a valid language, {{ value }} given.'; + private string $message = 'The {{ name }} value is not a valid language.'; public function __construct( private readonly string $code = self::ALPHA_2_CODE, @@ -30,11 +30,11 @@ public function __construct( 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); + throw new InvalidOptionException('code', self::CODE_OPTIONS); } if (!\is_string($value)) { - throw new UnexpectedTypeException('string', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'string'); } // keep original value for parameters diff --git a/src/Rule/Length.php b/src/Rule/Length.php index a4a6e9a..157fca7 100644 --- a/src/Rule/Length.php +++ b/src/Rule/Length.php @@ -3,10 +3,9 @@ namespace ProgrammatorDev\Validator\Rule; use ProgrammatorDev\Validator\Exception\LengthException; -use ProgrammatorDev\Validator\Exception\UnexpectedOptionException; +use ProgrammatorDev\Validator\Exception\InvalidOptionException; +use ProgrammatorDev\Validator\Exception\OptionDefinitionException; use ProgrammatorDev\Validator\Exception\UnexpectedTypeException; -use ProgrammatorDev\Validator\Exception\UnexpectedValueException; -use ProgrammatorDev\Validator\Validator; class Length extends AbstractRule implements RuleInterface { @@ -22,9 +21,9 @@ class Length extends AbstractRule implements RuleInterface /** @var ?callable */ private $normalizer; - private string $minMessage = 'The {{ name }} value should have {{ min }} characters or more, {{ numChars }} characters given.'; - private string $maxMessage = 'The {{ name }} value should have {{ max }} characters or less, {{ numChars }} characters given.'; - private string $exactMessage = 'The {{ name }} value should have exactly {{ min }} characters, {{ numChars }} characters given.'; + private string $minMessage = 'The {{ name }} value should have {{ min }} character(s) or more.'; + private string $maxMessage = 'The {{ name }} value should have {{ max }} character(s) or less.'; + private string $exactMessage = 'The {{ name }} value should have exactly {{ min }} characters.'; private string $charsetMessage = 'The {{ name }} value does not match the expected {{ charset }} charset.'; public function __construct( @@ -49,29 +48,21 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if ($this->min === null && $this->max === null) { - throw new UnexpectedValueException('At least one of the options "min" or "max" must be given.'); + throw new OptionDefinitionException('At least one of the "min" or "max" options must be specified.'); } - if ( - $this->min !== null - && $this->max !== null - && !Validator::greaterThanOrEqual($this->min)->validate($this->max) - ) { - throw new UnexpectedValueException('Maximum value must be greater than or equal to minimum value.'); - } - - $encodings = mb_list_encodings(); + $encodings = \mb_list_encodings(); if (!\in_array($this->charset, $encodings)) { - throw new UnexpectedOptionException('charset', $encodings, $this->charset); + throw new InvalidOptionException('charset', $encodings); } if (!\in_array($this->countUnit, self::COUNT_UNITS)) { - throw new UnexpectedOptionException('countUnit', self::COUNT_UNITS, $this->countUnit); + throw new InvalidOptionException('countUnit', self::COUNT_UNITS); } if (!\is_scalar($value) && !$value instanceof \Stringable) { - throw new UnexpectedTypeException('string|\Stringable', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'string|\Stringable'); } $value = (string) $value; @@ -80,7 +71,7 @@ public function assert(mixed $value, ?string $name = null): void $value = ($this->normalizer)($value); } - if (!mb_check_encoding($value, $this->charset)) { + if (!\mb_check_encoding($value, $this->charset)) { throw new LengthException( message: $this->charsetMessage, parameters: [ diff --git a/src/Rule/LessThan.php b/src/Rule/LessThan.php index fa5bcb9..27f6c83 100644 --- a/src/Rule/LessThan.php +++ b/src/Rule/LessThan.php @@ -6,7 +6,7 @@ class LessThan extends AbstractComparisonRule implements RuleInterface { - protected string $message = 'The {{ name }} value should be less than {{ constraint }}, {{ value }} given.'; + protected string $message = 'The {{ name }} value should be less than {{ constraint }}.'; public function __construct( protected readonly mixed $constraint, diff --git a/src/Rule/LessThanOrEqual.php b/src/Rule/LessThanOrEqual.php index f8f6626..6c6bf45 100644 --- a/src/Rule/LessThanOrEqual.php +++ b/src/Rule/LessThanOrEqual.php @@ -6,7 +6,7 @@ class LessThanOrEqual extends AbstractComparisonRule implements RuleInterface { - protected string $message = 'The {{ name }} value should be less than or equal to {{ constraint }}, {{ value }} given.'; + protected string $message = 'The {{ name }} value should be less than or equal to {{ constraint }}.'; public function __construct( protected readonly mixed $constraint, diff --git a/src/Rule/Locale.php b/src/Rule/Locale.php index dc6e506..6823e9f 100644 --- a/src/Rule/Locale.php +++ b/src/Rule/Locale.php @@ -8,7 +8,7 @@ class Locale extends AbstractRule implements RuleInterface { - private string $message = 'The {{ name }} value is not a valid locale, {{ value }} given.'; + private string $message = 'The {{ name }} value is not a valid locale.'; public function __construct( private readonly bool $canonicalize = false, @@ -21,7 +21,7 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if (!\is_string($value)) { - throw new UnexpectedTypeException('string', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'string'); } // keep original value for parameters diff --git a/src/Rule/NotBlank.php b/src/Rule/NotBlank.php index 112965b..068743b 100644 --- a/src/Rule/NotBlank.php +++ b/src/Rule/NotBlank.php @@ -9,7 +9,7 @@ class NotBlank extends AbstractRule implements RuleInterface { /** @var ?callable */ private $normalizer; - private string $message = 'The {{ name }} value should not be blank, {{ value }} given.'; + private string $message = 'The {{ name }} value should not be blank.'; public function __construct( ?callable $normalizer = null, diff --git a/src/Rule/NotNull.php b/src/Rule/NotNull.php new file mode 100644 index 0000000..e3e625e --- /dev/null +++ b/src/Rule/NotNull.php @@ -0,0 +1,30 @@ +message = $message ?? $this->message; + } + + public function assert(mixed $value, ?string $name = null): void + { + if (Validator::isNull()->validate($value) === true) { + throw new NotNullException( + message: $this->message, + parameters: [ + 'name' => $name + ] + ); + } + } +} \ No newline at end of file diff --git a/src/Rule/PasswordStrength.php b/src/Rule/PasswordStrength.php index fb0bd18..d93dccf 100644 --- a/src/Rule/PasswordStrength.php +++ b/src/Rule/PasswordStrength.php @@ -3,7 +3,7 @@ namespace ProgrammatorDev\Validator\Rule; use ProgrammatorDev\Validator\Exception\PasswordStrengthException; -use ProgrammatorDev\Validator\Exception\UnexpectedOptionException; +use ProgrammatorDev\Validator\Exception\InvalidOptionException; use ProgrammatorDev\Validator\Exception\UnexpectedTypeException; class PasswordStrength extends AbstractRule implements RuleInterface @@ -42,11 +42,11 @@ public function __construct( public function assert(#[\SensitiveParameter] mixed $value, ?string $name = null): void { if (!\in_array($this->minStrength, self::STRENGTH_OPTIONS)) { - throw new UnexpectedOptionException('minStrength', self::STRENGTH_OPTIONS, $this->minStrength); + throw new InvalidOptionException('minStrength', self::STRENGTH_OPTIONS); } if (!\is_string($value)) { - throw new UnexpectedTypeException('string', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'string'); } $minScore = self::STRENGTH_SCORE[$this->minStrength]; diff --git a/src/Rule/Range.php b/src/Rule/Range.php index faa4aec..e0a5997 100644 --- a/src/Rule/Range.php +++ b/src/Rule/Range.php @@ -4,7 +4,6 @@ use ProgrammatorDev\Validator\Exception\RangeException; use ProgrammatorDev\Validator\Exception\UnexpectedComparableException; -use ProgrammatorDev\Validator\Exception\UnexpectedValueException; use ProgrammatorDev\Validator\Rule\Util\ComparableTrait; use ProgrammatorDev\Validator\Validator; @@ -12,7 +11,7 @@ class Range extends AbstractRule implements RuleInterface { use ComparableTrait; - private string $message = 'The {{ name }} value should be between {{ min }} and {{ max }}, {{ value }} given.'; + private string $message = 'The {{ name }} value should be between {{ min }} and {{ max }}.'; public function __construct( private readonly mixed $min, @@ -26,14 +25,7 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if (!$this->isComparable($this->min, $this->max)) { - throw new UnexpectedComparableException( - get_debug_type($this->min), - get_debug_type($this->max) - ); - } - - if (!Validator::greaterThan($this->min)->validate($this->max)) { - throw new UnexpectedValueException('Maximum value must be greater than minimum value.'); + throw new UnexpectedComparableException($this->min, $this->max); } if (!Validator::greaterThanOrEqual($this->min)->lessThanOrEqual($this->max)->validate($value)) { diff --git a/src/Rule/Regex.php b/src/Rule/Regex.php index de3a250..6f1baa6 100644 --- a/src/Rule/Regex.php +++ b/src/Rule/Regex.php @@ -2,9 +2,9 @@ namespace ProgrammatorDev\Validator\Rule; +use ProgrammatorDev\Validator\Exception\InvalidOptionException; use ProgrammatorDev\Validator\Exception\RegexException; use ProgrammatorDev\Validator\Exception\UnexpectedTypeException; -use ProgrammatorDev\Validator\Exception\UnexpectedValueException; class Regex extends AbstractRule implements RuleInterface { @@ -26,7 +26,7 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if (!\is_scalar($value) && !$value instanceof \Stringable) { - throw new UnexpectedTypeException('string|\Stringable', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'string|\Stringable'); } $value = (string) $value; @@ -36,7 +36,7 @@ public function assert(mixed $value, ?string $name = null): void } if (($regex = @\preg_match($this->pattern, $value)) === false) { - throw new UnexpectedValueException('Invalid regular expression pattern.'); + throw new InvalidOptionException('pattern', 'The value should be a valid regular expression.'); } if ($this->match xor $regex) { diff --git a/src/Rule/Timezone.php b/src/Rule/Timezone.php index a2f5758..abd8693 100644 --- a/src/Rule/Timezone.php +++ b/src/Rule/Timezone.php @@ -2,14 +2,15 @@ namespace ProgrammatorDev\Validator\Rule; +use ProgrammatorDev\Validator\Exception\InvalidOptionException; +use ProgrammatorDev\Validator\Exception\OptionDefinitionException; use ProgrammatorDev\Validator\Exception\TimezoneException; -use ProgrammatorDev\Validator\Exception\UnexpectedValueException; use ProgrammatorDev\Validator\Exception\ValidationException; use ProgrammatorDev\Validator\Validator; class Timezone extends AbstractRule implements RuleInterface { - private string $message = 'The {{ name }} value is not a valid timezone, {{ value }} given.'; + private string $message = 'The {{ name }} value is not a valid timezone.'; public function __construct( private readonly int $timezoneGroup = \DateTimeZone::ALL, @@ -23,21 +24,21 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if ($this->timezoneGroup === \DateTimeZone::PER_COUNTRY) { - // Country code is required when using PER_COUNTRY timezone group + // country code is required when using PER_COUNTRY timezone group if ($this->countryCode === null) { - throw new UnexpectedValueException( - 'A country code is required when timezone group is "\DateTimeZone::PER_COUNTRY".' + throw new OptionDefinitionException( + 'The "countryCode" option should be specified when the "timezoneGroup" is "\DateTimeZone::PER_COUNTRY".' ); } - // Normalize country code + // normalize country code $this->countryCode = strtoupper($this->countryCode); try { - Validator::country()->assert($this->countryCode, 'country code'); + Validator::country()->assert($this->countryCode); } - catch (ValidationException $exception) { - throw new UnexpectedValueException($exception->getMessage()); + catch (ValidationException) { + throw new InvalidOptionException('countryCode'); } } diff --git a/src/Rule/Type.php b/src/Rule/Type.php index 7ca8499..3e97931 100644 --- a/src/Rule/Type.php +++ b/src/Rule/Type.php @@ -3,7 +3,7 @@ namespace ProgrammatorDev\Validator\Rule; use ProgrammatorDev\Validator\Exception\TypeException; -use ProgrammatorDev\Validator\Exception\UnexpectedOptionException; +use ProgrammatorDev\Validator\Exception\InvalidOptionException; class Type extends AbstractRule implements RuleInterface { @@ -69,7 +69,7 @@ class Type extends AbstractRule implements RuleInterface self::UPPERCASE => 'ctype_upper' ]; - private string $message = 'The {{ name }} value should be of type {{ constraint }}, {{ value }} given.'; + private string $message = 'The {{ name }} value should be of type {{ constraint }}.'; public function __construct( private readonly string|array $constraint, @@ -93,7 +93,7 @@ public function assert(mixed $value, ?string $name = null): void } if (!isset(self::TYPE_FUNCTIONS[$constraint]) && !\class_exists($constraint) && !\interface_exists($constraint)) { - throw new UnexpectedOptionException('type', \array_keys(self::TYPE_FUNCTIONS), $constraint); + throw new InvalidOptionException('type', \array_keys(self::TYPE_FUNCTIONS)); } } diff --git a/src/Rule/Url.php b/src/Rule/Url.php index cbf4231..6a53ba8 100644 --- a/src/Rule/Url.php +++ b/src/Rule/Url.php @@ -34,7 +34,7 @@ class Url extends AbstractRule implements RuleInterface /** @var ?callable */ private $normalizer; - private string $message = 'The {{ name }} value is not a valid URL address, {{ value }} given.'; + private string $message = 'The {{ name }} value is not a valid URL address.'; public function __construct( private readonly array $protocols = ['http', 'https'], @@ -50,7 +50,7 @@ public function __construct( public function assert(mixed $value, ?string $name = null): void { if (!\is_string($value)) { - throw new UnexpectedTypeException('string', get_debug_type($value)); + throw new UnexpectedTypeException($value, 'string'); } if ($this->normalizer !== null) { diff --git a/src/StaticValidatorInterface.php b/src/StaticValidatorInterface.php index 691032e..7b7ce50 100644 --- a/src/StaticValidatorInterface.php +++ b/src/StaticValidatorInterface.php @@ -6,6 +6,11 @@ interface StaticValidatorInterface { + public static function atLeastOneOf( + array $constraints, + ?string $message = null + ): ChainedValidatorInterface&Validator; + public static function blank( ?callable $normalizer = null, ?string $message = null @@ -79,6 +84,10 @@ public static function greaterThanOrEqual( ?string $message = null ): ChainedValidatorInterface&Validator; + public static function isNull( + ?string $message = null + ): ChainedValidatorInterface&Validator; + public static function language( string $code = 'alpha-2', ?string $message = null @@ -116,6 +125,18 @@ public static function notBlank( ?string $message = null ): ChainedValidatorInterface&Validator; + public static function isFalse( + ?string $message = null + ): ChainedValidatorInterface&Validator; + + public static function notNull( + ?string $message = null + ): ChainedValidatorInterface&Validator; + + public static function isTrue( + ?string $message = null + ): ChainedValidatorInterface&Validator; + public static function optional( Validator $validator ): ChainedValidatorInterface&Validator; diff --git a/src/Validator.php b/src/Validator.php index e1748d4..2b8eca1 100644 --- a/src/Validator.php +++ b/src/Validator.php @@ -6,12 +6,13 @@ use ProgrammatorDev\Validator\Exception\UnexpectedValueException; use ProgrammatorDev\Validator\Exception\ValidationException; use ProgrammatorDev\Validator\Factory\Factory; +use ProgrammatorDev\Validator\Rule\AbstractRule; use ProgrammatorDev\Validator\Rule\RuleInterface; /** * @mixin StaticValidatorInterface */ -class Validator implements RuleInterface +class Validator extends AbstractRule implements RuleInterface { /** @var RuleInterface[] */ private array $rules; @@ -59,18 +60,6 @@ public function assert(mixed $value, ?string $name = null): void } } - public function validate(mixed $value): bool - { - try { - $this->assert($value); - } - catch (ValidationException) { - return false; - } - - return true; - } - public function getRules(): array { return $this->rules; diff --git a/tests/AtLeastOneOfTest.php b/tests/AtLeastOneOfTest.php new file mode 100644 index 0000000..c4456c1 --- /dev/null +++ b/tests/AtLeastOneOfTest.php @@ -0,0 +1,73 @@ + [new AtLeastOneOf(['invalid']), 'string', $invalidOptionMessage]; + } + + public static function provideRuleFailureConditionData(): \Generator + { + $exception = AtLeastOneOfException::class; + $message = '/The (.*) value should satisfy at least one of the following constraints\: (.*)\./'; + + yield 'constraints' => [ + new AtLeastOneOf([ + new Validator(new IsFalse()), + new Validator(new Type('string'), new Length(10)), + ]), 'invalid', $exception, $message + ]; + } + + public static function provideRuleSuccessConditionData(): \Generator + { + yield 'constraints 1' => [ + new AtLeastOneOf([ + new Validator(new IsFalse()), + new Validator(new Type('string'), new Length(10)), + ]), + false + ]; + yield 'constraints 2' => [ + new AtLeastOneOf([ + new Validator(new IsFalse()), + new Validator(new Type('string'), new Length(10)), + ]), + 'valid string' + ]; + } + + public static function provideRuleMessageOptionData(): \Generator + { + yield 'message' => [ + new AtLeastOneOf( + constraints: [new Validator(new Blank())], + message: '{{ name }} | {{ value }} | {{ messages }}' + ), + 'string', + 'test | "string" | [1] The value should be blank.' + ]; + } +} \ No newline at end of file diff --git a/tests/BlankTest.php b/tests/BlankTest.php index 998cbfc..861400e 100644 --- a/tests/BlankTest.php +++ b/tests/BlankTest.php @@ -17,7 +17,7 @@ class BlankTest extends AbstractTest public static function provideRuleFailureConditionData(): \Generator { $exception = BlankException::class; - $message = '/The (.*) value should be blank, (.*) given\./'; + $message = '/The (.*) value should be blank\./'; yield 'true' => [new Blank(), true, $exception, $message]; diff --git a/tests/ChoiceTest.php b/tests/ChoiceTest.php index 3a54f6c..222b345 100644 --- a/tests/ChoiceTest.php +++ b/tests/ChoiceTest.php @@ -19,12 +19,9 @@ class ChoiceTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { $constraints = [1, 2, 3, 4, 5]; + $unexpectedTypeMessage = '/Expected value of type "array", "(.*)" given\./'; - $unexpectedMultipleMessage = '/Expected value of type "array", "(.*)" given\./'; - $unexpectedMinMaxMessage = '/Maximum value must be greater than or equal to minimum value\./'; - - yield 'multiple not array' => [new Choice($constraints, true), 1, $unexpectedMultipleMessage]; - yield 'min greater than max constraint' => [new Choice($constraints, true, 3, 2), [1, 2], $unexpectedMinMaxMessage]; + yield 'unexpected type multiple' => [new Choice($constraints, true), 1, $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator @@ -32,10 +29,10 @@ public static function provideRuleFailureConditionData(): \Generator $constraints = [1, 2, 3, 4, 5]; $exception = ChoiceException::class; - $message = '/The (.*) value is not a valid choice, (.*) given\. Accepted values are\: (.*)\./'; - $multipleMessage = '/The (.*) value has one or more invalid choices, (.*) given\. Accepted values are\: (.*)\./'; - $maxMessage = '/The (.*) value must have at most (.*) choices, (.*) choices given\./'; - $minMessage = '/The (.*) value must have at least (.*) choices, (.*) choices given\./'; + $message = '/The (.*) value is not a valid choice\. Accepted values are\: (.*)\./'; + $multipleMessage = '/The (.*) value has one or more invalid choices\. Accepted values are\: (.*)\./'; + $maxMessage = '/The (.*) value must have at most (.*) choice\(s\)\./'; + $minMessage = '/The (.*) value must have at least (.*) choice\(s\)\./'; yield 'invalid choice' => [new Choice($constraints), 10, $exception, $message]; yield 'invalid choice type' => [new Choice($constraints), '1', $exception, $message]; diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index 1c64ca1..a37449b 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -19,15 +19,15 @@ class CollectionTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedFieldValueMessage = '/At field (.*)\: (.*)\./'; + $invalidOptionMessage = '/The "fields" option is not valid. All values should be of type "ProgrammatorDev\\\Validator\\\Validator"./'; $unexpectedTypeMessage = '/Expected value of type "array\|\\\Traversable", "(.*)" given\./'; - yield 'invalid field value' => [ + yield 'invalid option fields' => [ new Collection(fields: ['field' => 'invalid']), ['field' => 'value'], - $unexpectedFieldValueMessage + $invalidOptionMessage ]; - yield 'invalid value type' => [ + yield 'unexpected type' => [ new Collection(fields: ['field' => Validator::notBlank()]), 'invalid', $unexpectedTypeMessage @@ -37,7 +37,7 @@ public static function provideRuleUnexpectedValueData(): \Generator public static function provideRuleFailureConditionData(): \Generator { $exception = CollectionException::class; - $notBlankMessage = '/The "(.*)" value should not be blank, "" given\./'; + $notBlankMessage = '/The "(.*)" value should not be blank\./'; $extraFieldsMessage = '/The (.*) field is not allowed\./'; $missingFieldsMessage = '/The (.*) field is missing\./'; @@ -120,7 +120,7 @@ public static function provideRuleMessageOptionData(): \Generator message: '{{ name }} | {{ field }} | {{ message }}' ), ['field' => ''], - 'test | "field" | The "field" value should not be blank, "" given.' + 'test | "field" | The "field" value should not be blank.' ]; yield 'extra fields message' => [ new Collection( diff --git a/tests/CountTest.php b/tests/CountTest.php index c498a94..b3a1ae3 100644 --- a/tests/CountTest.php +++ b/tests/CountTest.php @@ -18,13 +18,11 @@ class CountTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedMissingMinMaxMessage = '/At least one of the options "min" or "max" must be given\./'; - $unexpectedMinMaxMessage = '/Maximum value must be greater than or equal to minimum value\./'; + $optionDefinitionMessage = '/At least one of the "min" or "max" options must be specified\./'; $unexpectedTypeMessage = '/Expected value of type "array\|\\\Countable", "(.*)" given\./'; - yield 'missing min max' => [new Count(), [1, 2, 3], $unexpectedMissingMinMaxMessage]; - yield 'min greater than max constraint' => [new Count(min: 3, max: 2), [1, 2, 3], $unexpectedMinMaxMessage]; - yield 'invalid type value' => [new Count(min: 5, max: 10), 1, $unexpectedTypeMessage]; + yield 'option definition min max' => [new Count(), [1, 2, 3], $optionDefinitionMessage]; + yield 'unexpected type' => [new Count(min: 5, max: 10), 1, $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator @@ -32,9 +30,9 @@ public static function provideRuleFailureConditionData(): \Generator $value = [1, 2, 3, 4, 5]; $exception = CountException::class; - $minMessage = '/The (.*) value should contain (.*) elements or more, (.*) elements given\./'; - $maxMessage = '/The (.*) value should contain (.*) elements or less, (.*) elements given\./'; - $exactMessage = '/The (.*) value should contain exactly (.*) elements, (.*) elements given\./'; + $minMessage = '/The (.*) value should contain (.*) elements or more\./'; + $maxMessage = '/The (.*) value should contain (.*) elements or less\./'; + $exactMessage = '/The (.*) value should contain exactly (.*) elements\./'; yield 'min constraint' => [new Count(min: 10), $value, $exception, $minMessage]; yield 'max constraint' => [new Count(max: 2), $value, $exception, $maxMessage]; diff --git a/tests/CountryTest.php b/tests/CountryTest.php index 253f40b..6bae60f 100644 --- a/tests/CountryTest.php +++ b/tests/CountryTest.php @@ -18,17 +18,17 @@ class CountryTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedCodeMessage = '/Invalid code "(.*)"\. Accepted values are\: "(.*)"\./'; + $invalidOptionMessage = '/The "code" option is not valid\. Accepted values are\: "(.*)"\./'; $unexpectedTypeMessage = '/Expected value of type "string", (.*) given\./'; - yield 'invalid code' => [new Country('invalid'), 'pt', $unexpectedCodeMessage]; - yield 'invalid type' => [new Country(), 123, $unexpectedTypeMessage]; + yield 'invalid option code' => [new Country('invalid'), 'pt', $invalidOptionMessage]; + yield 'unexpected type' => [new Country(), 123, $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator { $exception = CountryException::class; - $message = '/The (.*) value is not a valid country, (.*) given\./'; + $message = '/The (.*) value is not a valid country\./'; yield 'default' => [new Country(), 'prt', $exception, $message]; yield 'alpha2' => [new Country(code: 'alpha-2'), 'prt', $exception, $message]; diff --git a/tests/CssColorTest.php b/tests/CssColorTest.php index 604bac0..a643a86 100644 --- a/tests/CssColorTest.php +++ b/tests/CssColorTest.php @@ -18,11 +18,11 @@ class CssColorTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedOptionMessage = '/Invalid (.*) "(.*)"\. Accepted values are\: "(.*)"./'; + $invalidOptionMessage = '/The "formats" option is not valid\. Accepted values are\: "(.*)"\./'; $unexpectedTypeMessage = '/Expected value of type "string", "(.*)" given\./'; - yield 'invalid option' => [new CssColor(formats: ['invalid']), '#123456', $unexpectedOptionMessage]; - yield 'invalid type' => [new CssColor(), 1, $unexpectedTypeMessage]; + yield 'invalid option format' => [new CssColor(formats: ['invalid']), '#123456', $invalidOptionMessage]; + yield 'unexpected type' => [new CssColor(), 1, $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator diff --git a/tests/DateTimeTest.php b/tests/DateTimeTest.php index 0b10101..565530c 100644 --- a/tests/DateTimeTest.php +++ b/tests/DateTimeTest.php @@ -20,7 +20,7 @@ public static function provideRuleUnexpectedValueData(): \Generator { $unexpectedTypeMessage = '/Expected value of type "string\|\\\Stringable", "(.*)" given\./'; - yield 'invalid value type' => [new DateTime(), ['2024-01-01 00:00:00'], $unexpectedTypeMessage]; + yield 'unexpected type' => [new DateTime(), ['2024-01-01 00:00:00'], $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator diff --git a/tests/EachKeyTest.php b/tests/EachKeyTest.php index 1d80eb1..4a75c18 100644 --- a/tests/EachKeyTest.php +++ b/tests/EachKeyTest.php @@ -22,24 +22,24 @@ class EachKeyTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { $unexpectedTypeMessage = '/Expected value of type "(.*)", "(.*)" given\./'; - $unexpectedPropagationMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; + $unexpectedComparableMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; - yield 'invalid value type' => [ + yield 'unexpected type' => [ new EachKey(new Validator(new Type('string'))), 'invalid', $unexpectedTypeMessage ]; - yield 'unexpected value propagation' => [ + yield 'unexpected comparable' => [ new EachKey(new Validator(new GreaterThan(10))), ['key1' => 1], - $unexpectedPropagationMessage + $unexpectedComparableMessage ]; } public static function provideRuleFailureConditionData(): \Generator { $exception = EachKeyException::class; - $message = '/Invalid key\: The (.*) key value should be of type (.*), (.*) given\./'; + $message = '/Invalid key (.*)\: The value should be of type (.*)\./'; yield 'invalid array element' => [ new EachKey(new Validator(new Type('string'))), @@ -75,7 +75,7 @@ public static function provideRuleMessageOptionData(): \Generator message: '{{ name }} | {{ value }} | {{ key }} | {{ element }} | {{ message }}' ), ['key1' => 1, 'key2' => 2, 1 => 3], - 'test | [1, 2, 3] | 1 | 3 | The test key value should be of type "string", 1 given.' + 'test | [1, 2, 3] | 1 | 3 | The value should be of type "string".' ]; } } \ No newline at end of file diff --git a/tests/EachValueTest.php b/tests/EachValueTest.php index 9bbf239..b2ae48d 100644 --- a/tests/EachValueTest.php +++ b/tests/EachValueTest.php @@ -22,24 +22,24 @@ class EachValueTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { $unexpectedTypeMessage = '/Expected value of type "(.*)", "(.*)" given\./'; - $unexpectedPropagationMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; + $unexpectedComparableMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; - yield 'invalid value type' => [ + yield 'unexpected type' => [ new EachValue(new Validator(new NotBlank())), 'invalid', $unexpectedTypeMessage ]; - yield 'unexpected value propagation' => [ + yield 'unexpected comparable' => [ new EachValue(new Validator(new GreaterThan(10))), ['a'], - $unexpectedPropagationMessage + $unexpectedComparableMessage ]; } public static function provideRuleFailureConditionData(): \Generator { $exception = EachValueException::class; - $message = '/At key (.*)\: The (.*) value should not be blank, (.*) given\./'; + $message = '/At key (.*)\: The (.*) value should not be blank\./'; yield 'invalid array element' => [ new EachValue(new Validator(new NotBlank())), @@ -75,7 +75,7 @@ public static function provideRuleMessageOptionData(): \Generator message: '{{ name }} | {{ value }} | {{ key }} | {{ element }} | {{ message }}' ), [1, 2, ''], - 'test | [1, 2, ""] | 2 | "" | The test value should not be blank, "" given.' + 'test | [1, 2, ""] | 2 | "" | The test value should not be blank.' ]; } } \ No newline at end of file diff --git a/tests/EmailTest.php b/tests/EmailTest.php index b9a82b8..ae3fe7a 100644 --- a/tests/EmailTest.php +++ b/tests/EmailTest.php @@ -18,17 +18,17 @@ class EmailTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedOptionMessage = '/Invalid (.*) "(.*)"\. Accepted values are\: "(.*)"./'; + $invalidOptionMessage = '/The "mode" option is not valid\. Accepted values are\: "(.*)"./'; $unexpectedTypeMessage = '/Expected value of type "string", "(.*)" given\./'; - yield 'invalid option' => [new Email('invalid'), 'test@example.com', $unexpectedOptionMessage]; - yield 'invalid type' => [new Email(), 1, $unexpectedTypeMessage]; + yield 'invalid option mode' => [new Email('invalid'), 'test@example.com', $invalidOptionMessage]; + yield 'unexpected type' => [new Email(), 1, $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator { $exception = EmailException::class; - $message = '/The (.*) value is not a valid email address, (.*) given\./'; + $message = '/The (.*) value is not a valid email address\./'; yield 'html5' => [new Email('html5'), 'invalid', $exception, $message]; yield 'html5 without tld' => [new Email('html5'), 'test@example', $exception, $message]; diff --git a/tests/GreaterThanOrEqualTest.php b/tests/GreaterThanOrEqualTest.php index e06084f..11c9b1f 100644 --- a/tests/GreaterThanOrEqualTest.php +++ b/tests/GreaterThanOrEqualTest.php @@ -18,21 +18,21 @@ class GreaterThanOrEqualTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedTypeMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; + $unexpectedComparableMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; - yield 'datetime constraint with int value' => [new GreaterThanOrEqual(new \DateTime()), 10, $unexpectedTypeMessage]; - yield 'datetime constraint with float value' => [new GreaterThanOrEqual(new \DateTime()), 1.0, $unexpectedTypeMessage]; - yield 'datetime constraint with string value' => [new GreaterThanOrEqual(new \DateTime()), 'a', $unexpectedTypeMessage]; - yield 'int constraint with string value' => [new GreaterThanOrEqual(10), 'a', $unexpectedTypeMessage]; - yield 'float constraint with string value' => [new GreaterThanOrEqual(1.0), 'a', $unexpectedTypeMessage]; - yield 'array constraint' => [new GreaterThanOrEqual([10]), 10, $unexpectedTypeMessage]; - yield 'null constraint' => [new GreaterThanOrEqual(null), 10, $unexpectedTypeMessage]; + yield 'datetime constraint with int value' => [new GreaterThanOrEqual(new \DateTime()), 10, $unexpectedComparableMessage]; + yield 'datetime constraint with float value' => [new GreaterThanOrEqual(new \DateTime()), 1.0, $unexpectedComparableMessage]; + yield 'datetime constraint with string value' => [new GreaterThanOrEqual(new \DateTime()), 'a', $unexpectedComparableMessage]; + yield 'int constraint with string value' => [new GreaterThanOrEqual(10), 'a', $unexpectedComparableMessage]; + yield 'float constraint with string value' => [new GreaterThanOrEqual(1.0), 'a', $unexpectedComparableMessage]; + yield 'array constraint' => [new GreaterThanOrEqual([10]), 10, $unexpectedComparableMessage]; + yield 'null constraint' => [new GreaterThanOrEqual(null), 10, $unexpectedComparableMessage]; } public static function provideRuleFailureConditionData(): \Generator { $exception = GreaterThanOrEqualException::class; - $message = '/The (.*) value should be greater than or equal to (.*), (.*) given\./'; + $message = '/The (.*) value should be greater than or equal to (.*)\./'; yield 'datetime' => [new GreaterThanOrEqual(new \DateTime('today')), new \DateTime('yesterday'), $exception, $message]; yield 'int' => [new GreaterThanOrEqual(10), 1, $exception, $message]; diff --git a/tests/GreaterThanTest.php b/tests/GreaterThanTest.php index 26bf8bb..7ffa77f 100644 --- a/tests/GreaterThanTest.php +++ b/tests/GreaterThanTest.php @@ -18,21 +18,21 @@ class GreaterThanTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedTypeMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; + $unexpectedComparableMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; - yield 'datetime constraint with int value' => [new GreaterThan(new \DateTime()), 10, $unexpectedTypeMessage]; - yield 'datetime constraint with float value' => [new GreaterThan(new \DateTime()), 1.0, $unexpectedTypeMessage]; - yield 'datetime constraint with string value' => [new GreaterThan(new \DateTime()), 'a', $unexpectedTypeMessage]; - yield 'int constraint with string value' => [new GreaterThan(10), 'a', $unexpectedTypeMessage]; - yield 'float constraint with string value' => [new GreaterThan(1.0), 'a', $unexpectedTypeMessage]; - yield 'array constraint' => [new GreaterThan([10]), 10, $unexpectedTypeMessage]; - yield 'null constraint' => [new GreaterThan(null), 10, $unexpectedTypeMessage]; + yield 'datetime constraint with int value' => [new GreaterThan(new \DateTime()), 10, $unexpectedComparableMessage]; + yield 'datetime constraint with float value' => [new GreaterThan(new \DateTime()), 1.0, $unexpectedComparableMessage]; + yield 'datetime constraint with string value' => [new GreaterThan(new \DateTime()), 'a', $unexpectedComparableMessage]; + yield 'int constraint with string value' => [new GreaterThan(10), 'a', $unexpectedComparableMessage]; + yield 'float constraint with string value' => [new GreaterThan(1.0), 'a', $unexpectedComparableMessage]; + yield 'array constraint' => [new GreaterThan([10]), 10, $unexpectedComparableMessage]; + yield 'null constraint' => [new GreaterThan(null), 10, $unexpectedComparableMessage]; } public static function provideRuleFailureConditionData(): \Generator { $exception = GreaterThanException::class; - $message = '/The (.*) value should be greater than (.*), (.*) given\./'; + $message = '/The (.*) value should be greater than (.*)\./'; yield 'datetime' => [new GreaterThan(new \DateTime('today')), new \DateTime('yesterday'), $exception, $message]; yield 'same datetime' => [new GreaterThan(new \DateTime('today')), new \DateTime('today'), $exception, $message]; diff --git a/tests/IsFalseTest.php b/tests/IsFalseTest.php new file mode 100644 index 0000000..7fd843e --- /dev/null +++ b/tests/IsFalseTest.php @@ -0,0 +1,43 @@ + [new IsFalse(), 1, $exception, $message]; + yield 'string' => [new IsFalse(), 'string', $exception, $message]; + yield 'true' => [new IsFalse(), true, $exception, $message]; + yield 'array' => [new IsFalse(), [], $exception, $message]; + } + + public static function provideRuleSuccessConditionData(): \Generator + { + yield 'false' => [new IsFalse(), false]; + } + + public static function provideRuleMessageOptionData(): \Generator + { + yield 'message' => [ + new IsFalse( + message: '{{ name }} | {{ value }}' + ), + true, + 'test | true' + ]; + } +} \ No newline at end of file diff --git a/tests/IsNullTest.php b/tests/IsNullTest.php new file mode 100644 index 0000000..2144b1f --- /dev/null +++ b/tests/IsNullTest.php @@ -0,0 +1,43 @@ + [new IsNull(), 1, $exception, $message]; + yield 'string' => [new IsNull(), 'string', $exception, $message]; + yield 'bool' => [new IsNull(), true, $exception, $message]; + yield 'array' => [new IsNull(), [], $exception, $message]; + } + + public static function provideRuleSuccessConditionData(): \Generator + { + yield 'null' => [new IsNull(), null]; + } + + public static function provideRuleMessageOptionData(): \Generator + { + yield 'message' => [ + new IsNull( + message: '{{ name }} | {{ value }}' + ), + 'string', + 'test | "string"' + ]; + } +} \ No newline at end of file diff --git a/tests/IsTrueTest.php b/tests/IsTrueTest.php new file mode 100644 index 0000000..cc92ac2 --- /dev/null +++ b/tests/IsTrueTest.php @@ -0,0 +1,43 @@ + [new IsTrue(), 1, $exception, $message]; + yield 'string' => [new IsTrue(), 'string', $exception, $message]; + yield 'false' => [new IsTrue(), false, $exception, $message]; + yield 'array' => [new IsTrue(), [], $exception, $message]; + } + + public static function provideRuleSuccessConditionData(): \Generator + { + yield 'true' => [new IsTrue(), true]; + } + + public static function provideRuleMessageOptionData(): \Generator + { + yield 'message' => [ + new IsTrue( + message: '{{ name }} | {{ value }}' + ), + false, + 'test | false' + ]; + } +} \ No newline at end of file diff --git a/tests/LanguageTest.php b/tests/LanguageTest.php index fc9741d..6345144 100644 --- a/tests/LanguageTest.php +++ b/tests/LanguageTest.php @@ -18,17 +18,17 @@ class LanguageTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedCodeMessage = '/Invalid code "(.*)"\. Accepted values are\: "(.*)"\./'; + $invalidOptionMessage = '/The "code" option is not valid\. Accepted values are\: "(.*)"\./'; $unexpectedTypeMessage = '/Expected value of type "string", (.*) given\./'; - yield 'invalid code' => [new Language('invalid'), 'pt', $unexpectedCodeMessage]; - yield 'invalid type' => [new Language(), 123, $unexpectedTypeMessage]; + yield 'invalid option code' => [new Language('invalid'), 'pt', $invalidOptionMessage]; + yield 'unexpected type' => [new Language(), 123, $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator { $exception = LanguageException::class; - $message = '/The (.*) value is not a valid language, (.*) given\./'; + $message = '/The (.*) value is not a valid language\./'; yield 'default' => [new Language(), 'prt', $exception, $message]; yield 'alpha2' => [new Language(code: 'alpha-2'), 'por', $exception, $message]; diff --git a/tests/LengthTest.php b/tests/LengthTest.php index fc796db..c92bd50 100644 --- a/tests/LengthTest.php +++ b/tests/LengthTest.php @@ -20,16 +20,14 @@ public static function provideRuleUnexpectedValueData(): \Generator { $value = 'abcde'; - $unexpectedMissingMinMaxMessage = '/At least one of the options "min" or "max" must be given\./'; - $unexpectedMinMaxMessage = '/Maximum value must be greater than or equal to minimum value\./'; - $unexpectedOptionMessage = '/Invalid (.*) "(.*)"\. Accepted values are\: "(.*)"\./'; + $optionDefinitionMessage = '/At least one of the "min" or "max" options must be specified\./'; + $invalidOptionMessage = '/The "(.*)" option is not valid\. Accepted values are\: "(.*)"\./'; $unexpectedTypeMessage = '/Expected value of type "string\|\\\Stringable", "(.*)" given\./'; - yield 'missing min max' => [new Length(), $value, $unexpectedMissingMinMaxMessage]; - yield 'min greater than max constraint' => [new Length(min: 3, max: 2), $value, $unexpectedMinMaxMessage]; - yield 'invalid charset value' => [new Length(min: 2, charset: 'INVALID'), $value, $unexpectedOptionMessage]; - yield 'invalid count unit value' => [new Length(min: 2, countUnit: 'invalid'), $value, $unexpectedOptionMessage]; - yield 'invalid value type' => [new Length(min: 2), [$value], $unexpectedTypeMessage]; + yield 'option definition min max' => [new Length(), $value, $optionDefinitionMessage]; + yield 'invalid option charset' => [new Length(min: 2, charset: 'INVALID'), $value, $invalidOptionMessage]; + yield 'invalid option count unit' => [new Length(min: 2, countUnit: 'invalid'), $value, $invalidOptionMessage]; + yield 'unexpected type' => [new Length(min: 2), [$value], $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator @@ -37,9 +35,9 @@ public static function provideRuleFailureConditionData(): \Generator $value = 'abcde'; $exception = LengthException::class; - $minMessage = '/The (.*) value should have (.*) characters or more, (.*) characters given\./'; - $maxMessage = '/The (.*) value should have (.*) characters or less, (.*) characters given\./'; - $exactMessage = '/The (.*) value should have exactly (.*) characters, (.*) characters given\./'; + $minMessage = '/The (.*) value should have (.*) character\(s\) or more\./'; + $maxMessage = '/The (.*) value should have (.*) character\(s\) or less\./'; + $exactMessage = '/The (.*) value should have exactly (.*) characters\./'; $charsetMessage = '/The (.*) value does not match the expected (.*) charset\./'; yield 'min constraint' => [new Length(min: 10), $value, $exception, $minMessage]; diff --git a/tests/LessThanOrEqualTest.php b/tests/LessThanOrEqualTest.php index 2469a1b..fe7c937 100644 --- a/tests/LessThanOrEqualTest.php +++ b/tests/LessThanOrEqualTest.php @@ -18,21 +18,21 @@ class LessThanOrEqualTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedTypeMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; + $unexpectedComparableMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; - yield 'datetime constraint with int value' => [new LessThanOrEqual(new \DateTime()), 10, $unexpectedTypeMessage]; - yield 'datetime constraint with float value' => [new LessThanOrEqual(new \DateTime()), 1.0, $unexpectedTypeMessage]; - yield 'datetime constraint with string value' => [new LessThanOrEqual(new \DateTime()), 'a', $unexpectedTypeMessage]; - yield 'int constraint with string value' => [new LessThanOrEqual(10), 'a', $unexpectedTypeMessage]; - yield 'float constraint with string value' => [new LessThanOrEqual(1.0), 'a', $unexpectedTypeMessage]; - yield 'array constraint' => [new LessThanOrEqual([10]), 10, $unexpectedTypeMessage]; - yield 'null constraint' => [new LessThanOrEqual(null), 10, $unexpectedTypeMessage]; + yield 'datetime constraint with int value' => [new LessThanOrEqual(new \DateTime()), 10, $unexpectedComparableMessage]; + yield 'datetime constraint with float value' => [new LessThanOrEqual(new \DateTime()), 1.0, $unexpectedComparableMessage]; + yield 'datetime constraint with string value' => [new LessThanOrEqual(new \DateTime()), 'a', $unexpectedComparableMessage]; + yield 'int constraint with string value' => [new LessThanOrEqual(10), 'a', $unexpectedComparableMessage]; + yield 'float constraint with string value' => [new LessThanOrEqual(1.0), 'a', $unexpectedComparableMessage]; + yield 'array constraint' => [new LessThanOrEqual([10]), 10, $unexpectedComparableMessage]; + yield 'null constraint' => [new LessThanOrEqual(null), 10, $unexpectedComparableMessage]; } public static function provideRuleFailureConditionData(): \Generator { $exception = LessThanOrEqualException::class; - $message = '/The (.*) value should be less than or equal to (.*), (.*) given\./'; + $message = '/The (.*) value should be less than or equal to (.*)\./'; yield 'datetime' => [new LessThanOrEqual(new \DateTime('today')), new \DateTime('tomorrow'), $exception, $message]; yield 'int' => [new LessThanOrEqual(10), 20, $exception, $message]; diff --git a/tests/LessThanTest.php b/tests/LessThanTest.php index 4b65c46..dddaeb2 100644 --- a/tests/LessThanTest.php +++ b/tests/LessThanTest.php @@ -18,21 +18,21 @@ class LessThanTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedTypeMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; + $unexpectedComparableMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; - yield 'datetime constraint with int value' => [new LessThan(new \DateTime()), 10, $unexpectedTypeMessage]; - yield 'datetime constraint with float value' => [new LessThan(new \DateTime()), 1.0, $unexpectedTypeMessage]; - yield 'datetime constraint with string value' => [new LessThan(new \DateTime()), 'a', $unexpectedTypeMessage]; - yield 'int constraint with string value' => [new LessThan(10), 'a', $unexpectedTypeMessage]; - yield 'float constraint with string value' => [new LessThan(1.0), 'a', $unexpectedTypeMessage]; - yield 'array constraint' => [new LessThan([10]), 10, $unexpectedTypeMessage]; - yield 'null constraint' => [new LessThan(null), 10, $unexpectedTypeMessage]; + yield 'datetime constraint with int value' => [new LessThan(new \DateTime()), 10, $unexpectedComparableMessage]; + yield 'datetime constraint with float value' => [new LessThan(new \DateTime()), 1.0, $unexpectedComparableMessage]; + yield 'datetime constraint with string value' => [new LessThan(new \DateTime()), 'a', $unexpectedComparableMessage]; + yield 'int constraint with string value' => [new LessThan(10), 'a', $unexpectedComparableMessage]; + yield 'float constraint with string value' => [new LessThan(1.0), 'a', $unexpectedComparableMessage]; + yield 'array constraint' => [new LessThan([10]), 10, $unexpectedComparableMessage]; + yield 'null constraint' => [new LessThan(null), 10, $unexpectedComparableMessage]; } public static function provideRuleFailureConditionData(): \Generator { $exception = LessThanException::class; - $message = '/The (.*) value should be less than (.*), (.*) given\./'; + $message = '/The (.*) value should be less than (.*)\./'; yield 'datetime' => [new LessThan(new \DateTime('today')), new \DateTime('tomorrow'), $exception, $message]; yield 'same datetime' => [new LessThan(new \DateTime('today')), new \DateTime('today'), $exception, $message]; diff --git a/tests/LocaleTest.php b/tests/LocaleTest.php index bb5a697..43ee0c0 100644 --- a/tests/LocaleTest.php +++ b/tests/LocaleTest.php @@ -20,13 +20,13 @@ public static function provideRuleUnexpectedValueData(): \Generator { $unexpectedTypeMessage = '/Expected value of type "string", (.*) given\./'; - yield 'invalid type' => [new Locale(), 123, $unexpectedTypeMessage]; + yield 'unexpected type' => [new Locale(), 123, $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator { $exception = LocaleException::class; - $message = '/The (.*) value is not a valid locale, (.*) given\./'; + $message = '/The (.*) value is not a valid locale\./'; yield 'invalid' => [new Locale(), 'invalid', $exception, $message]; yield 'uncanonicalized 1' => [new Locale(), 'pt_pt', $exception, $message]; diff --git a/tests/NotBlankTest.php b/tests/NotBlankTest.php index 26ac662..1a14546 100644 --- a/tests/NotBlankTest.php +++ b/tests/NotBlankTest.php @@ -17,7 +17,7 @@ class NotBlankTest extends AbstractTest public static function provideRuleFailureConditionData(): \Generator { $exception = NotBlankException::class; - $message = '/The (.*) value should not be blank, (.*) given\./'; + $message = '/The (.*) value should not be blank\./'; yield 'null' => [new NotBlank(), null, $exception, $message]; yield 'false' => [new NotBlank(), false, $exception, $message]; diff --git a/tests/NotNullTest.php b/tests/NotNullTest.php new file mode 100644 index 0000000..c73d9b7 --- /dev/null +++ b/tests/NotNullTest.php @@ -0,0 +1,43 @@ + [new NotNull(), null, $exception, $message]; + } + + public static function provideRuleSuccessConditionData(): \Generator + { + yield 'int' => [new NotNull(), 1]; + yield 'string' => [new NotNull(), 'string']; + yield 'bool' => [new NotNull(), true]; + yield 'array' => [new NotNull(), []]; + } + + public static function provideRuleMessageOptionData(): \Generator + { + yield 'message' => [ + new NotNull( + message: '{{ name }}' + ), + null, + 'test' + ]; + } +} \ No newline at end of file diff --git a/tests/OptionalTest.php b/tests/OptionalTest.php index 564bb2a..5e8d3b4 100644 --- a/tests/OptionalTest.php +++ b/tests/OptionalTest.php @@ -22,7 +22,7 @@ public static function provideRuleFailureConditionData(): \Generator ), '', NotBlankException::class, - '/The (.*) value should not be blank, (.*) given\./' + '/The (.*) value should not be blank\./' ]; } diff --git a/tests/PasswordStrengthTest.php b/tests/PasswordStrengthTest.php index 593768f..3797b67 100644 --- a/tests/PasswordStrengthTest.php +++ b/tests/PasswordStrengthTest.php @@ -18,11 +18,11 @@ class PasswordStrengthTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedOptionMessage = '/Invalid (.*) "(.*)"\. Accepted values are\: "(.*)"\./'; + $invalidOptionMessage = '/The "minStrength" option is not valid\. Accepted values are\: "(.*)"\./'; $unexpectedTypeMessage = '/Expected value of type "string", "(.*)" given\./'; - yield 'invalid min strength' => [new PasswordStrength(minStrength: 'invalid'), 'password', $unexpectedOptionMessage]; - yield 'invalid value type' => [new PasswordStrength(), 123, $unexpectedTypeMessage]; + yield 'invalid option min strength' => [new PasswordStrength(minStrength: 'invalid'), 'password', $invalidOptionMessage]; + yield 'unexpected type' => [new PasswordStrength(), 123, $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator diff --git a/tests/RangeTest.php b/tests/RangeTest.php index 452c0fb..0a0346c 100644 --- a/tests/RangeTest.php +++ b/tests/RangeTest.php @@ -18,25 +18,21 @@ class RangeTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedTypeMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; - $unexpectedMinMaxMessage = '/Maximum value must be greater than minimum value\./'; + $unexpectedComparableMessage = '/Cannot compare a type "(.*)" with a type "(.*)"\./'; - yield 'datetime constraint with int constraint' => [new Range(new \DateTime(), 10), new \DateTime(), $unexpectedTypeMessage]; - yield 'datetime constraint with float constraint' => [new Range(new \DateTime(), 10.0), new \DateTime(), $unexpectedTypeMessage]; - yield 'datetime constraint with string constraint' => [new Range(new \DateTime(), 'a'), new \DateTime(), $unexpectedTypeMessage]; - yield 'int constraint with string constraint' => [new Range(10, 'a'), 10, $unexpectedTypeMessage]; - yield 'float constraint with string constraint' => [new Range(1.0, 'a'), 1.0, $unexpectedTypeMessage]; - yield 'array constraint' => [new Range([10], 10), 10, $unexpectedTypeMessage]; - yield 'null constraint' => [new Range(null, 10), 10, $unexpectedTypeMessage]; - - yield 'min greater than max constraint' => [new Range(10, 9), 10, $unexpectedMinMaxMessage]; - yield 'same min and max constraint' => [new Range(10, 10), 10, $unexpectedMinMaxMessage]; + yield 'datetime constraint with int constraint' => [new Range(new \DateTime(), 10), new \DateTime(), $unexpectedComparableMessage]; + yield 'datetime constraint with float constraint' => [new Range(new \DateTime(), 10.0), new \DateTime(), $unexpectedComparableMessage]; + yield 'datetime constraint with string constraint' => [new Range(new \DateTime(), 'a'), new \DateTime(), $unexpectedComparableMessage]; + yield 'int constraint with string constraint' => [new Range(10, 'a'), 10, $unexpectedComparableMessage]; + yield 'float constraint with string constraint' => [new Range(1.0, 'a'), 1.0, $unexpectedComparableMessage]; + yield 'array constraint' => [new Range([10], 10), 10, $unexpectedComparableMessage]; + yield 'null constraint' => [new Range(null, 10), 10, $unexpectedComparableMessage]; } public static function provideRuleFailureConditionData(): \Generator { $exception = RangeException::class; - $message = '/The (.*) value should be between (.*) and (.*), (.*) given\./'; + $message = '/The (.*) value should be between (.*) and (.*)\./'; yield 'min datetime' => [new Range(new \DateTime('today'), new \DateTime('tomorrow')), new \DateTime('yesterday'), $exception, $message]; yield 'min int' => [new Range(10, 20), 1, $exception, $message]; diff --git a/tests/RegexTest.php b/tests/RegexTest.php index 5c3cd03..84dbb77 100644 --- a/tests/RegexTest.php +++ b/tests/RegexTest.php @@ -18,11 +18,11 @@ class RegexTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedPatternMessage = '/Invalid regular expression pattern\./'; + $invalidOptionMessage = '/The "pattern" option is not valid\. The value should be a valid regular expression\./'; $unexpectedTypeMessage = '/Expected value of type "string\|\\\Stringable", "(.*)" given\./'; - yield 'invalid pattern' => [new Regex('invalid'), 'abc', $unexpectedPatternMessage]; - yield 'invalid value type' => [new Regex('/[a-z]/'), ['abc'], $unexpectedTypeMessage]; + yield 'invalid option pattern' => [new Regex('invalid'), 'abc', $invalidOptionMessage]; + yield 'unexpected type' => [new Regex('/[a-z]/'), ['abc'], $unexpectedTypeMessage]; } public static function provideRuleFailureConditionData(): \Generator diff --git a/tests/TimezoneTest.php b/tests/TimezoneTest.php index 4a0b914..84873c9 100644 --- a/tests/TimezoneTest.php +++ b/tests/TimezoneTest.php @@ -18,25 +18,25 @@ class TimezoneTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedMissingCountryCodeMessage = '/A country code is required when timezone group is "\\\DateTimeZone::PER_COUNTRY"\./'; - $unexpectedCountryCodeMessage = '/The (.*) value is not a valid country, (.*) given\./'; + $optionDefinitionMessage = '/The "countryCode" option should be specified when the "timezoneGroup" is "\\\DateTimeZone::PER_COUNTRY"\./'; + $invalidOptionMessage = '/The "countryCode" option is not valid\./'; - yield 'missing country code' => [ + yield 'option definition country code' => [ new Timezone(\DateTimeZone::PER_COUNTRY), 'Europe/Lisbon', - $unexpectedMissingCountryCodeMessage + $optionDefinitionMessage ]; - yield 'invalid country code' => [ + yield 'invalid option country code' => [ new Timezone(\DateTimeZone::PER_COUNTRY, 'prt'), 'Europe/Lisbon', - $unexpectedCountryCodeMessage + $invalidOptionMessage ]; } public static function provideRuleFailureConditionData(): \Generator { $exception = TimezoneException::class; - $message = '/The (.*) value is not a valid timezone, (.*) given\./'; + $message = '/The (.*) value is not a valid timezone\./'; yield 'invalid timezone' => [new Timezone(), 'Invalid/Timezone', $exception, $message]; yield 'not of timezone group' => [new Timezone(\DateTimeZone::AFRICA), 'Europe/Lisbon', $exception, $message]; diff --git a/tests/TypeTest.php b/tests/TypeTest.php index 9e4d11e..82a231c 100644 --- a/tests/TypeTest.php +++ b/tests/TypeTest.php @@ -18,7 +18,7 @@ class TypeTest extends AbstractTest public static function provideRuleUnexpectedValueData(): \Generator { - $unexpectedTypeMessage = '/Invalid (.*) "(.*)"\. Accepted values are: "(.*)"\./'; + $unexpectedTypeMessage = '/The "type" option is not valid\. Accepted values are: "(.*)"\./'; yield 'invalid type' => [new Type('invalid'), 'string', $unexpectedTypeMessage]; } @@ -26,7 +26,7 @@ public static function provideRuleUnexpectedValueData(): \Generator public static function provideRuleFailureConditionData(): \Generator { $exception = TypeException::class; - $message = '/The (.*) value should be of type (.*), (.*) given\./'; + $message = '/The (.*) value should be of type (.*)\./'; yield 'bool' => [new Type('bool'), 'invalid', $exception, $message]; yield 'boolean' => [new Type('boolean'), 'invalid', $exception, $message]; diff --git a/tests/UrlTest.php b/tests/UrlTest.php index 770c5e2..7b3fcce 100644 --- a/tests/UrlTest.php +++ b/tests/UrlTest.php @@ -26,7 +26,7 @@ public static function provideRuleUnexpectedValueData(): \Generator public static function provideRuleFailureConditionData(): \Generator { $exception = UrlException::class; - $message = '/The (.*) value is not a valid URL address, (.*) given\./'; + $message = '/The (.*) value is not a valid URL address\./'; yield 'invalid url' => [new URL(), 'invalid', $exception, $message]; yield 'unallowed protocol' => [new URL(protocols: ['https']), 'http://test.com', $exception, $message];