Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
HexColor
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
2 / 2
9
100.00% covered (success)
100.00%
1 / 1
 validate
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 passes
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3declare(strict_types=1);
4
5namespace DevToolbelt\LaravelEloquentPlus\Validators;
6
7use Closure;
8use Illuminate\Contracts\Validation\ValidationRule;
9
10/**
11 * Validation rule for hexadecimal color codes.
12 *
13 * Validates both short (3 characters) and full (6 characters) hex color formats.
14 * The leading hash (#) is optional.
15 *
16 * Usage:
17 * ```php
18 * $rules = [
19 *     'background_color' => ['required', new HexColor()],
20 *     'text_color' => ['nullable', new HexColor()],
21 * ];
22 * ```
23 *
24 * Valid formats:
25 * - "#FFF" or "FFF" (short format)
26 * - "#FFFFFF" or "FFFFFF" (full format)
27 * - Case insensitive: "#fff", "#AbC123"
28 *
29 * Invalid formats:
30 * - "#FFFFF" (5 characters)
31 * - "#GGG" (invalid hex characters)
32 * - "red" (color names not supported)
33 *
34 * @package DevToolbelt\LaravelEloquentPlus\Validators
35 */
36final class HexColor implements ValidationRule
37{
38    /**
39     * Regular expression pattern for valid hex color codes.
40     *
41     * Matches: #FFF, FFF, #FFFFFF, FFFFFF (case insensitive)
42     */
43    private const string PATTERN = '/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/';
44
45    /**
46     * Run the validation rule.
47     *
48     * @param string $attribute The attribute name being validated
49     * @param mixed $value The value to validate
50     * @param Closure(string, string|null=): \Illuminate\Translation\PotentiallyTranslatedString $fail
51     * @return void
52     */
53    public function validate(string $attribute, mixed $value, Closure $fail): void
54    {
55        if ($value === null || $value === '') {
56            return;
57        }
58
59        if (!is_string($value)) {
60            $fail('The :attribute must be a valid hexadecimal color.');
61            return;
62        }
63
64        if (!preg_match(self::PATTERN, $value)) {
65            $fail('The :attribute must be a valid hexadecimal color (e.g., #FFF or #FFFFFF).');
66        }
67    }
68
69    /**
70     * Determine if the validation rule passes.
71     *
72     * This method can be used for simple boolean validation checks,
73     * useful for Validator::extend() registration.
74     *
75     * @param mixed $value The value to validate
76     * @return bool True if valid, false otherwise
77     */
78    public function passes(mixed $value): bool
79    {
80        if ($value === null || $value === '') {
81            return true;
82        }
83
84        if (!is_string($value)) {
85            return false;
86        }
87
88        return (bool) preg_match(self::PATTERN, $value);
89    }
90}