Lines 100.00% 8 / 8
Methods 100.00% 2 / 2
Classes 100.00% 1 / 1
Name Lines Methods CRAP
 withMessage 100.00% 2 / 2 100.00% 1 / 1 1
 message 100.00% 6 / 6 100.00% 1 / 1 3
 defaultMessage n/a 0 / 0 n/a 0 / 0 0
 validate n/a 0 / 0 n/a 0 / 0 0
18abstract class Constraint
19{
20    /**
21     * Optional custom message, either a literal string or a closure that
22     * receives the {@see FieldContext} and returns a string.
23     *
24     * @var string|\Closure(FieldContext): string|null
25     */
26    private string|\Closure|null $customMessage = null;
27
28    /**
29     * Override the default error message for this constraint.
30     *
31     * The message may be a plain string or a closure that receives the
32     * {@see FieldContext} and returns the message string, allowing messages
33     * to be built dynamically from the field name or value.
34     *
35     * @param string|\Closure(FieldContext): string $message The custom message or a closure producing it.
36     * @return static The current instance for method chaining.
37     */
38    final public function withMessage(string|\Closure $message): static
39    {
40        $this->customMessage = $message;
41        return $this;
42    }
43
44    /**
45     * Resolve the error message for a given field context.
46     *
47     * Returns the custom message if one was set via {@see withMessage()},
48     * otherwise falls back to {@see defaultMessage()}. Closures are invoked
49     * with the supplied context. Returns null when there is no message to
50     * report — used by combinators whose child constraints already recorded
51     * their specific errors on the result.
52     *
53     * @param FieldContext $ctx The context of the field being validated.
54     * @return string|null The resolved error message, or null to skip.
55     */
56    final public function message(FieldContext $ctx): ?string
57    {
58        $message = $this->customMessage ?? $this->defaultMessage();
59
60        if ($message === null) {
61            return null;
62        }
63
64        if ($message instanceof \Closure) {
65            return call_user_func($message, $ctx);
66        }
67
68        return $message;
69    }
70
71    /**
72     * Provide the default error message for this constraint.
73     *
74     * May return a plain string, a closure that receives the
75     * {@see FieldContext} and returns the message string, or null to signal
76     * that no message should be reported (used when a child constraint has
77     * already recorded its specific error).
78     *
79     * @return string|\Closure(FieldContext): ?string|null The default message, a closure producing it, or null to skip.
80     */
81    abstract protected function defaultMessage(): string|\Closure|null;
82
83    /**
84     * Validate a field value.
85     *
86     * Implementations inspect the value exposed by the context and return
87     * whether it satisfies the rule. They may also call
88     * {@see FieldContext::normalize()} to transform the value (e.g. casting a
89     * numeric string to a number) before it is stored in the result.
90     *
91     * @param FieldContext $ctx The context of the field being validated.
92     * @return bool True if the value is valid, false otherwise.
93     */
94    abstract public function validate(FieldContext $ctx): bool;
95}