Skip to content

Commit 2600db3

Browse files
committed
Introduce Trimmed validator
This commit introduces the `Trimmed` validator that ensures a string cannot start or end with a list of specific values. The default values used are a selected list of Unicode invisible characters. To support this change, the StartsWith and EndsWith validators were modified so they can also support multiple values to check for. While StartsWith and EndsWith are more generic, and also perform start-of-array and end-of-array kinds of checks, Trimmed is more focused on string inputs, which tailors to a more specific use case.
1 parent e924e39 commit 2600db3

29 files changed

Lines changed: 480 additions & 55 deletions

docs/validators.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ In this page you will find a list of validators by their category.
4949

5050
**Objects**: [Attributes][] - [Instance][] - [ObjectType][] - [Property][] - [PropertyExists][] - [PropertyOptional][]
5151

52-
**Strings**: [Alnum][] - [Alpha][] - [Base64][] - [Charset][] - [Consonant][] - [Contains][] - [ContainsAny][] - [ContainsCount][] - [Control][] - [Digit][] - [Emoji][] - [EndsWith][] - [Format][] - [Graph][] - [HexRgbColor][] - [In][] - [Json][] - [Lowercase][] - [Phone][] - [PostalCode][] - [Printable][] - [Punct][] - [Regex][] - [Slug][] - [Sorted][] - [Space][] - [Spaced][] - [StartsWith][] - [StringType][] - [StringVal][] - [Uppercase][] - [Uuid][] - [Version][] - [Vowel][] - [Xdigit][]
52+
**Strings**: [Alnum][] - [Alpha][] - [Base64][] - [Charset][] - [Consonant][] - [Contains][] - [ContainsAny][] - [ContainsCount][] - [Control][] - [Digit][] - [Emoji][] - [EndsWith][] - [Format][] - [Graph][] - [HexRgbColor][] - [In][] - [Json][] - [Lowercase][] - [Phone][] - [PostalCode][] - [Printable][] - [Punct][] - [Regex][] - [Slug][] - [Sorted][] - [Space][] - [Spaced][] - [StartsWith][] - [StringType][] - [StringVal][] - [Trimmed][] - [Uppercase][] - [Uuid][] - [Version][] - [Vowel][] - [Xdigit][]
5353

5454
**Structures**: [Attributes][] - [Key][] - [KeyExists][] - [KeyOptional][] - [KeySet][] - [Property][] - [PropertyExists][] - [PropertyOptional][]
5555

@@ -203,6 +203,7 @@ In this page you will find a list of validators by their category.
203203
- [Templated][] - `v::templated('You must provide a valid email', v::email())->assert('foo@bar.com');`
204204
- [Time][] - `v::time()->assert('00:00:00');`
205205
- [Tld][] - `v::tld()->assert('com');`
206+
- [Trimmed][] - `v::trimmed()->assert('lorem ipsum');`
206207
- [TrueVal][] - `v::trueVal()->assert(true);`
207208
- [Undef][] - `v::undef()->assert('');`
208209
- [UndefOr][] - `v::undefOr(v::alpha())->assert('');`
@@ -351,7 +352,7 @@ In this page you will find a list of validators by their category.
351352
[Sorted]: validators/Sorted.md "Validates whether the input is sorted in a certain order or not."
352353
[Space]: validators/Space.md "Validates whether the input contains only whitespaces characters."
353354
[Spaced]: validators/Spaced.md "Validates if a string contains at least one whitespace (spaces, tabs, or line breaks);"
354-
[StartsWith]: validators/StartsWith.md "Validates whether the input starts with a given value."
355+
[StartsWith]: validators/StartsWith.md "Validates whether the input starts with one of the given values."
355356
[StringType]: validators/StringType.md "Validates whether the type of an input is string or not."
356357
[StringVal]: validators/StringVal.md "Validates whether the input can be used as a string."
357358
[SubdivisionCode]: validators/SubdivisionCode.md "Validates subdivision country codes according to ISO 3166-2."
@@ -360,6 +361,7 @@ In this page you will find a list of validators by their category.
360361
[Templated]: validators/Templated.md "Defines a validator with a custom message template."
361362
[Time]: validators/Time.md "Validates whether an input is a time or not. The `$format` argument should be in"
362363
[Tld]: validators/Tld.md "Validates whether the input is a top-level domain."
364+
[Trimmed]: validators/Trimmed.md "Validates whether the input string does not start or end with the given values."
363365
[TrueVal]: validators/TrueVal.md "Validates if a value is considered as `true`."
364366
[Undef]: validators/Undef.md "Validates if the given input is undefined. By _undefined_ we consider `null` or an empty string (`''`)."
365367
[UndefOr]: validators/UndefOr.md "Validates the input using a defined validator when the input is not `null` or an empty string (`''`)."

docs/validators/EndsWith.md

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,38 @@ SPDX-FileContributor: Henrique Moody <henriquemoody@gmail.com>
88
# EndsWith
99

1010
- `EndsWith(mixed $endValue)`
11+
- `EndsWith(mixed $endValue, mixed ...$endValues)`
1112

1213
This validator is similar to `Contains()`, but validates
13-
only if the value is at the end of the input.
14+
only if one of the values is at the end of the input. Only
15+
string inputs and string end values are checked; non‑string
16+
values are considered invalid but will not produce PHP errors
17+
thanks to internal type guards.
1418

15-
For strings:
19+
For strings (non-string inputs are always rejected):
1620

1721
```php
1822
v::endsWith('ipsum')->assert('lorem ipsum');
1923
// Validation passes successfully
24+
25+
v::endsWith(', PhD', ', doctor')->assert('Jane Doe, PhD');
26+
// Validation passes successfully
2027
```
2128

2229
For arrays:
2330

2431
```php
2532
v::endsWith('ipsum')->assert(['lorem', 'ipsum']);
2633
// Validation passes successfully
34+
35+
v::endsWith('.', ';')->assert(['this', 'is', 'a', 'tokenized', 'phrase', '.']);
36+
// Validation passes successfully
37+
38+
v::endsWith('.', ';')->assert(['this', 'is', 'a', 'tokenized', 'phrase']);
39+
// → `["this", "is", "a", "tokenized", "phrase"]` must end with "." or ";"
2740
```
2841

29-
Message template for this validator includes `{{endValue}}`.
42+
Message template for this validator includes `{{endValue}}` and `{{endValues}}`.
3043

3144
## Templates
3245

@@ -37,12 +50,20 @@ Message template for this validator includes `{{endValue}}`.
3750
| `default` | {{subject}} must end with {{endValue}} |
3851
| `inverted` | {{subject}} must not end with {{endValue}} |
3952

53+
### `EndsWith::TEMPLATE_MULTIPLE_VALUES`
54+
55+
| Mode | Template |
56+
| ---------: | :------------------------------------------------------- |
57+
| `default` | {{subject}} must end with {{endValues&#124;list:or}} |
58+
| `inverted` | {{subject}} must not end with {{endValues&#124;list:or}} |
59+
4060
## Template placeholders
4161

4262
| Placeholder | Description |
4363
| ----------- | ---------------------------------------------------------------- |
44-
| `endValue` | |
4564
| `subject` | The validated input or the custom validator name (if specified). |
65+
| `endValue` | The value that will be checked to be at the end of the input. |
66+
| `endValues` | Additional values to check. |
4667

4768
## Categorization
4869

@@ -53,6 +74,7 @@ Message template for this validator includes `{{endValue}}`.
5374

5475
| Version | Description |
5576
| ------: | :---------------------------------- |
77+
| 3.1.0 | Added support for multiple values |
5678
| 3.0.0 | Case-insensitive comparison removed |
5779
| 0.3.9 | Created |
5880

@@ -62,3 +84,4 @@ Message template for this validator includes `{{endValue}}`.
6284
- [In](In.md)
6385
- [Regex](Regex.md)
6486
- [StartsWith](StartsWith.md)
87+
- [Trimmed](Trimmed.md)

docs/validators/StartsWith.md

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ SPDX-FileContributor: Henrique Moody <henriquemoody@gmail.com>
99
# StartsWith
1010

1111
- `StartsWith(mixed $startValue)`
12+
- `StartsWith(mixed $startValue, mixed ...$startValues)`
1213

13-
Validates whether the input starts with a given value.
14+
Validates whether the input starts with one of the given values.
1415

1516
This validator is similar to [Contains](Contains.md), but validates only
1617
if the value is at the beginning of the input.
@@ -20,16 +21,25 @@ For strings:
2021
```php
2122
v::startsWith('lorem')->assert('lorem ipsum');
2223
// Validation passes successfully
24+
25+
v::startsWith('Dr.', 'Mr.')->assert('Dr. Jane Doe');
26+
// Validation passes successfully
2327
```
2428

2529
For arrays:
2630

2731
```php
2832
v::startsWith('lorem')->assert(['lorem', 'ipsum']);
2933
// Validation passes successfully
34+
35+
v::startsWith(0, 1)->assert([0, 1, 2, 3]);
36+
// Validation passes successfully
37+
38+
v::startsWith(0, 1)->assert([1, 2, 3]);
39+
// Validation passes successfully
3040
```
3141

32-
Message template for this validator includes `{{startValue}}`.
42+
Message template for this validator includes `{{startValue}}` and `{{startValues}}`.
3343

3444
## Templates
3545

@@ -40,12 +50,20 @@ Message template for this validator includes `{{startValue}}`.
4050
| `default` | {{subject}} must start with {{startValue}} |
4151
| `inverted` | {{subject}} must not start with {{startValue}} |
4252

53+
### `StartsWith::TEMPLATE_MULTIPLE_VALUES`
54+
55+
| Mode | Template |
56+
| ---------: | :----------------------------------------------------------- |
57+
| `default` | {{subject}} must start with {{startValues&#124;list:or}} |
58+
| `inverted` | {{subject}} must not start with {{startValues&#124;list:or}} |
59+
4360
## Template placeholders
4461

45-
| Placeholder | Description |
46-
| ------------ | ---------------------------------------------------------------- |
47-
| `subject` | The validated input or the custom validator name (if specified). |
48-
| `startValue` | |
62+
| Placeholder | Description |
63+
| ------------- | ---------------------------------------------------------------- |
64+
| `subject` | The validated input or the custom validator name (if specified). |
65+
| `startValue` | The value that will be checked to be at the start of the input. |
66+
| `startValues` | Additional values to check. |
4967

5068
## Categorization
5169

@@ -56,6 +74,7 @@ Message template for this validator includes `{{startValue}}`.
5674

5775
| Version | Description |
5876
| ------: | :---------------------------------- |
77+
| 3.1.0 | Added support for multiple values |
5978
| 3.0.0 | Case-insensitive comparison removed |
6079
| 0.3.9 | Created |
6180

@@ -65,3 +84,4 @@ Message template for this validator includes `{{startValue}}`.
6584
- [EndsWith](EndsWith.md)
6685
- [In](In.md)
6786
- [Regex](Regex.md)
87+
- [Trimmed](Trimmed.md)

docs/validators/Trimmed.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<!--
2+
SPDX-License-Identifier: MIT
3+
SPDX-FileCopyrightText: (c) Respect Project Contributors
4+
-->
5+
6+
# Trimmed
7+
8+
- `Trimmed()`
9+
- `Trimmed(string ...$trimValues)`
10+
11+
Validates whether the input string does not start or end with the given values.
12+
13+
When no values are provided, this validator uses a default list of Unicode invisible characters (including regular whitespace, non-breaking spaces, and zero-width characters).
14+
15+
With the default values:
16+
17+
```php
18+
v::trimmed()->assert('lorem ipsum');
19+
// Validation passes successfully
20+
21+
v::trimmed()->assert("\u{200B}lorem");
22+
// → "​lorem" must not contain leading or trailing whitespace
23+
```
24+
25+
With custom values:
26+
27+
```php
28+
v::trimmed('Dr.', 'Mr.', 'PhD.')->assert('John');
29+
// Validation passes successfully
30+
31+
v::trimmed('Dr.', 'Mr.', 'PhD.')->assert('Dr. John');
32+
// → "Dr. John" must not contain leading or trailing "Dr.", "Mr.", or "PhD."
33+
34+
v::trimmed('Dr.', 'Mr.', ', PhD')->assert('John Doe, PhD');
35+
// → "John Doe, PhD" must not contain leading or trailing "Dr.", "Mr.", or ", PhD"
36+
```
37+
38+
This validator composes [StartsWith](StartsWith.md) and [EndsWith](EndsWith.md).
39+
40+
## Templates
41+
42+
### `Trimmed::TEMPLATE_STANDARD`
43+
44+
| Mode | Template |
45+
| ---------: | :---------------------------------------------------------- |
46+
| `default` | {{subject}} must not contain leading or trailing whitespace |
47+
| `inverted` | {{subject}} must contain leading or trailing whitespace |
48+
49+
### `Trimmed::TEMPLATE_CUSTOM`
50+
51+
| Mode | Template |
52+
| ---------: | :--------------------------------------------------------------------------- |
53+
| `default` | {{subject}} must not contain leading or trailing {{trimValues&#124;list:or}} |
54+
| `inverted` | {{subject}} must contain leading or trailing {{trimValues&#124;list:or}} |
55+
56+
## Template placeholders
57+
58+
| Placeholder | Description |
59+
| ------------ | ---------------------------------------------------------------- |
60+
| `subject` | The validated input or the custom validator name (if specified). |
61+
| `trimValues` | The values that will be checked at start end end of input. |
62+
63+
## Categorization
64+
65+
- Strings
66+
67+
## Changelog
68+
69+
| Version | Description |
70+
| ------: | :---------- |
71+
| 3.1.0 | Created |
72+
73+
## See Also
74+
75+
- [EndsWith](EndsWith.md)
76+
- [Space](Space.md)
77+
- [Spaced](Spaced.md)
78+
- [StartsWith](StartsWith.md)

src/Mixins/AllBuilder.php

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Mixins/AllChain.php

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Mixins/Builder.php

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Mixins/Chain.php

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)