You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Create ShortCircuit validator and ShortCircuitable interface
This commit introduces a mechanism for validators to return early once
the validation outcome is determined, rather than evaluating all child
validators.
The ShortCircuit validator evaluates validators sequentially and stops
at the first failure, similar to how PHP's && operator works. This is
useful when later validators depend on earlier ones passing, or when
you want only the first error message.
The ShortCircuitCapable interface allows composite validators (AllOf,
AnyOf, OneOf, NoneOf, Each, All) to implement their own short-circuit
logic.
Why "ShortCircuit" instead of "FailFast":
The name "FailFast" was initially considered but proved misleading.
While AllOf stops on failure (fail fast), AnyOf stops on success
(succeed fast), and OneOf stops on the second success. The common
behavior is not about failing quickly, but about returning as soon as
the outcome is determined—which is exactly what short-circuit
evaluation means. This terminology is familiar to developers from
boolean operators (&& and ||), making the behavior immediately
understandable.
Co-authored-by: Alexandre Gomes Gaigalas <alganet@gmail.com>
Assisted-by: Claude Code (Opus 4.5)
Copy file name to clipboardExpand all lines: docs/feature-guide.md
+12-2Lines changed: 12 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,12 +27,20 @@ Note that you can combine multiple validators for a complex validation.
27
27
28
28
### Validating using exceptions
29
29
30
-
The `assert()` method throws an exception when validation fails. You can handle those exceptions with `try/catch` for more robust error handling.
30
+
The `assert()` method throws an exception when validation fails. It evaluates all validators in the chain and collects every error before throwing. You can handle those exceptions with `try/catch` for more robust error handling.
31
31
32
32
```php
33
33
v::intType()->positive()->assert($input);
34
34
```
35
35
36
+
The `check()` method also throws an exception when validation fails, but it stops at the first failure instead of collecting all errors. Internally, it wraps the chain in a `ShortCircuit` validator.
37
+
38
+
```php
39
+
v::intType()->positive()->check($input);
40
+
```
41
+
42
+
The difference is visible when multiple validators fail. With `assert()`, you get all error messages; with `check()`, you get only the first one.
43
+
36
44
### Validating using results
37
45
38
46
You can validate data and handle the result manually without using exceptions:
-**Grouped validation**: Combine validators with AND/OR logic using [AllOf](validators/AllOf.md), [AnyOf](validators/AnyOf.md), [NoneOf](validators/NoneOf.md), [OneOf](validators/OneOf.md).
132
140
-**Iteration**: Validate every item in a collection with [Each](validators/Each.md).
133
141
-**Length, Min, Max**: Validate derived values with [Length](validators/Length.md), [Min](validators/Min.md), [Max](validators/Max.md).
134
-
-**Special cases**: Handle dynamic rules with [Factory](validators/Factory.md), short-circuit on first failure with [Circuit](validators/Circuit.md), or transform input before validation with [After](validators/After.md).
142
+
-**Special cases**: Handle dynamic rules with [Factory](validators/Factory.md), selectively short-circuit on first failure with [ShortCircuit](validators/ShortCircuit.md), or transform input before validation with [After](validators/After.md).
143
+
144
+
Note: While `check()` automatically short-circuits the entire chain, the `ShortCircuit` validator gives you fine-grained control over which specific group of validators should stop at the first failure. Use `check()` when you want the whole chain to fail fast, and `ShortCircuit` when you want only a specific part of your validation to fail fast while the rest continues collecting errors.
The `ValidatorBuilder::assert()` method throws a `ValidationException` when validation fails. This exception provides detailed feedback on what went wrong.
10
+
Both `ValidatorBuilder::assert()` and `ValidatorBuilder::check()` throw a `ValidationException` when validation fails. This exception provides detailed feedback on what went wrong.
11
+
12
+
The difference between the two methods is that `assert()` evaluates all validators in the chain and collects every error, while `check()` stops at the first failure (using `ShortCircuit` internally).
11
13
12
14
## The `ValidationException`
13
15
@@ -21,6 +23,16 @@ try {
21
23
}
22
24
```
23
25
26
+
The same applies to `check()`:
27
+
28
+
```php
29
+
try {
30
+
v::alnum()->lowercase()->check($input);
31
+
} catch (InvalidArgumentException $exception) {
32
+
echo $exception->getMessage(); // Only the first failure
33
+
}
34
+
```
35
+
24
36
### Helpful stack traces
25
37
26
38
When an exception is thrown, PHP reports where it was *created*, not where it was *caused*. In most validation libraries that means stack traces point deep inside library internals. You end up hunting through the trace to find your actual code.
Copy file name to clipboardExpand all lines: docs/migrating-from-v2-to-v3.md
+30-35Lines changed: 30 additions & 35 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -69,30 +69,24 @@ In 2.x, `validate()` returned a boolean. In 3.0, it returns a `ResultQuery` obje
69
69
}
70
70
```
71
71
72
-
#### `check()`removed,`assert()`unified
72
+
#### `check()`and`assert()`behavior changes
73
73
74
-
In 2.x, there were two exception-based methods:
74
+
In 2.x, both `check()` and `assert()` threw validator-specific exception classes (e.g., `IntTypeException`), which were children of `ValidationException`. The difference was that `check()` threw these exceptions directly and stopped at the first failure, while `assert()` threw a `NestedValidationException` (also a child of `ValidationException`) that collected all errors and provided methods like `getFullMessage()` and `getMessages()` not available on the base `ValidationException`.
In 3.0, both are unified into `assert()`, which throws `ValidationException`:
76
+
In 3.0, validator-specific exception classes and `NestedValidationException` no longer exist. Both `check()` and `assert()` throw a unified `ValidationException` that includes `getMessage()`, `getFullMessage()`, and `getMessages()`. The behavioral distinction is preserved: `check()` still fails fast (stopping at the first failure, using `ShortCircuit` internally), while `assert()` collects all errors.
0 commit comments