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
25final 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

18trait 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}