Lines
100.00%
25 / 25
Methods
100.00%
3 / 3
Classes
100.00%
1 / 1
| Name | Lines | Methods | CRAP | ||||
|---|---|---|---|---|---|---|---|
| __construct | 100.00% | 7 / 7 | 100.00% | 1 / 1 | 4 | ||
| validate | 100.00% | 12 / 12 | 100.00% | 1 / 1 | 1 | ||
| [Lucent\Validation\Concerns\RecordsConstraintFailure] recordConstraintFailure | 100.00% | 6 / 6 | 100.00% | 1 / 1 | 3 | ||
| 25 | final class Validator | |
| 26 | { | |
| 27 | use RecordsConstraintFailure; | |
| 28 | ||
| 29 | /** | |
| 30 | * The top-level constraint applied to the data payload. | |
| 31 | */ | |
| 32 | private readonly Constraint $constraint; | |
| 33 | ||
| 34 | /** | |
| 35 | * Create a validator from a top-level constraint or a flat constraint map. | |
| 36 | * | |
| 37 | * @param Constraint|array<string, Constraint> $constraints A single top-level | |
| 38 | * constraint, or a map of constraints keyed by field name (wrapped in a Shape). | |
| 39 | * @throws \InvalidArgumentException If any value is not a {@see Constraint} instance. | |
| 40 | */ | |
| 41 | public function __construct(Constraint|array $constraints) | |
| 42 | { | |
| 43 | if ($constraints instanceof Constraint) { | |
| 44 | $this->constraint = $constraints; | |
| 45 | return; | |
| 46 | } | |
| 47 | ||
| 48 | foreach ($constraints as $constraint) { | |
| 49 | if (!($constraint instanceof Constraint)) { | |
| 50 | throw new \InvalidArgumentException('Constraint must be an instance of ' . Constraint::class); | |
| 51 | } | |
| 52 | } | |
| 53 | ||
| 54 | $this->constraint = Shape::object($constraints); | |
| 55 | } | |
| 56 | ||
| 57 | /** | |
| 58 | * Validate a data payload against the configured constraints. | |
| 59 | * | |
| 60 | * The top-level constraint is applied to the data. Failed constraints | |
| 61 | * record an error message on the result; successful constraints may | |
| 62 | * normalize the value, which is stored on the result. | |
| 63 | * | |
| 64 | * Accepts any value, so a top-level scalar constraint can validate a | |
| 65 | * plain value directly (e.g. `new Validator(new Length(min: 3))->validate('abc')`). | |
| 66 | * | |
| 67 | * @param mixed $body The data to validate (e.g. a parsed request body, or | |
| 68 | * a plain scalar for a top-level constraint). | |
| 69 | * @param array<string, \Psr\Http\Message\UploadedFileInterface> $files Optional | |
| 70 | * uploaded files keyed by field name, for file constraints. | |
| 71 | * @param array<string, mixed> $context Optional per-validation values (e.g. | |
| 72 | * the originating ServerRequest, the authenticated user) exposed to | |
| 73 | * constraints via {@see FieldContext::get()}. | |
| 74 | * @return Result The validation result containing errors and validated values. | |
| 75 | */ | |
| 76 | public function validate(mixed $body, array $files = [], array $context = []): Result | |
| 77 | { | |
| 78 | $result = new Result(); | |
| 79 | ||
| 80 | $ctx = new FieldContext( | |
| 81 | '', | |
| 82 | $body, | |
| 83 | true, | |
| 84 | $result, | |
| 85 | $files, | |
| 86 | $body, | |
| 87 | $context, | |
| 88 | ); | |
| 89 | ||
| 90 | $this->recordConstraintFailure($this->constraint, $ctx); | |
| 91 | ||
| 92 | return $result; | |
| 93 | } | |
| 94 | } |
From Lucent\Validation\Concerns\RecordsConstraintFailure
| 18 | trait RecordsConstraintFailure | |
| 19 | { | |
| 20 | /** | |
| 21 | * Validate a constraint and record its error if it fails. | |
| 22 | * | |
| 23 | * Runs the constraint against the context. On failure, resolves the | |
| 24 | * constraint's message and records it on the result at the context's | |
| 25 | * field path, unless the message is null (meaning a child already | |
| 26 | * recorded its specific error). | |
| 27 | * | |
| 28 | * @param Constraint $constraint The constraint to validate. | |
| 29 | * @param FieldContext $ctx The context of the field being validated. | |
| 30 | * @return bool True if the constraint passed, false otherwise. | |
| 31 | */ | |
| 32 | protected function recordConstraintFailure(Constraint $constraint, FieldContext $ctx): bool | |
| 33 | { | |
| 34 | if ($constraint->validate($ctx)) { | |
| 35 | return true; | |
| 36 | } | |
| 37 | ||
| 38 | $message = $constraint->message($ctx); | |
| 39 | if ($message !== null) { | |
| 40 | $ctx->result->addError($ctx->field, $message); | |
| 41 | } | |
| 42 | ||
| 43 | return false; | |
| 44 | } | |
| 45 | } |