Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
Rule
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
3 / 3
13
100.00% covered (success)
100.00%
1 / 1
 parse
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
8
 isHasClass
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 isHasOneClass
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@noladev.com>
7 * @copyright  Copyright (c) 2009-2025 NOLA Interactive, LLC.
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Validator;
15
16use Pop\Utils\Str;
17
18/**
19 * Validator rule class
20 *
21 * @category   Pop
22 * @package    Pop\Validator
23 * @author     Nick Sagona, III <dev@noladev.com>
24 * @copyright  Copyright (c) 2009-2025 NOLA Interactive, LLC.
25 * @license    http://www.popphp.org/license     New BSD License
26 * @version    4.5.0
27 */
28class Rule
29{
30
31    /**
32     * "Has" classes
33     * @var array
34     */
35    protected static array $hasClasses = [
36        'HasOneThatEquals', 'HasOnlyOneThatEquals', 'HasCountEqual', 'HasCountGreaterThanEqual',
37        'HasCountGreaterThan', 'HasCountLessThanEqual', 'HasCountLessThan', 'HasCountNotEqual'
38    ];
39
40    /**
41     * "Has one" classes
42     * @var array
43     */
44    protected static array $hasOneClasses = [
45        'HasOne', 'HasOnlyOne'
46    ];
47
48    /**
49     * Parse rule
50     *
51     * @param  string  $rule
52     * @param  ?string $prefix
53     * @throws \InvalidArgumentException
54     * @return array
55     */
56    public static function parse(string $rule, ?string $prefix = 'Pop\Validator\\'): array
57    {
58        $ruleSet = array_map('trim', explode(':', $rule));
59
60        if (count($ruleSet) < 2) {
61            throw new \InvalidArgumentException(
62                'Error: The rule is invalid. It must have at least a field and a validator, e.g. username:not_empty.'
63            );
64        }
65
66        $field     = $ruleSet[0];
67        $validator = Str::snakeCaseToTitleCase($ruleSet[1]);
68        $value     = (!empty($ruleSet[2])) ? $ruleSet[2] : null;
69        $message   = (!empty($ruleSet[3])) ? $ruleSet[3] : null;
70
71        if (!class_exists($prefix . $validator)) {
72            throw new \InvalidArgumentException("Error: The validator class '" . $prefix . $validator . "' does not exist.");
73        }
74
75        if (str_contains($rule, ',')) {
76            $value = array_filter(array_map('trim', explode(',', $value)));
77        }
78
79        if (self::isHasClass($validator, false, $prefix)) {
80            $value = [$field => $value];
81        } else if (self::isHasOneClass($validator, $prefix)) {
82            $value = $field;
83        }
84
85        return [
86            'field'     => $field,
87            'validator' => $validator,
88            'value'     => $value,
89            'message'   => $message,
90        ];
91    }
92
93    /**
94     * Is a "has" class
95     *
96     * @param  string  $class
97     * @param  bool    $both
98     * @param  ?string $prefix
99     * @return bool
100     */
101    public static function isHasClass(string $class, bool $both = false, ?string $prefix = 'Pop\Validator\\'): bool
102    {
103        if (str_starts_with($class, $prefix)) {
104            $class = substr($class, strlen($prefix));
105        }
106
107        $hasClasses = ($both) ? array_merge(self::$hasClasses, self::$hasOneClasses) : self::$hasClasses;
108
109        return in_array($class, $hasClasses);
110    }
111
112    /**
113     * Is a "has" class
114     *
115     * @param  string  $class
116     * @param  ?string $prefix
117     * @return bool
118     */
119    public static function isHasOneClass(string $class, ?string $prefix = 'Pop\Validator\\'): bool
120    {
121        if (str_starts_with($class, $prefix)) {
122            $class = substr($class, strlen($prefix));
123        }
124
125        return in_array($class, self::$hasOneClasses);
126    }
127
128}