refactor(Validator): improve rule argument parsing

This commit is contained in:
kj
2026-09-03 14:20:02 -03:00
parent b8dd1fd0f6
commit 15df7c8d96

View File

@@ -66,6 +66,30 @@ class Validator
return true;
}
/**
* Splits a single rule string into its name and its raw argument string.
*
* The rule name is everything before the first ':'. The remainder is
* returned verbatim (it is NOT split further here) so that rules whose
* arguments legitimately contain ':' or ',' - such as regex or mimes -
* are preserved intact. The caller decides how to split the arguments.
*
* @param string $rule The rule to parse. Ex: "regex:/^a,b$/" or "enum:a,b".
*
* @return array A two element array: [name, rawArguments]. When the rule
* has no parameters, rawArguments is an empty string.
*/
public static function parseRule(string $rule): array
{
$separator = strpos($rule, ':');
if ($separator === false) {
return [$rule, ''];
}
return [substr($rule, 0, $separator), substr($rule, $separator + 1)];
}
/**
* Checks if a rule is met.
*
@@ -77,15 +101,46 @@ class Validator
*/
public static function checkRule(mixed $subject, string $rule): bool
{
$arguments = preg_split('/[:,]/', $rule);
$rule = [static::class, $arguments[0]];
$arguments[0] = $subject;
[$name, $rawArguments] = static::parseRule($rule);
if (is_callable($rule)) {
return call_user_func_array($rule, $arguments);
$method = [static::class, $name];
$arguments = array_merge([$subject], static::splitArguments($name, $rawArguments));
if (is_callable($method)) {
return call_user_func_array($method, $arguments);
}
throw new Exception('Bad rule: "' . preg_split('/::/', $rule)[1] . '"');
throw new Exception('Bad rule: "' . $name . '"');
}
/**
* Splits a rule's raw argument string into the arguments to pass to the
* rule's method (the subject is not included).
*
* Most rules take a comma-separated list of values (e.g. "enum:a,b").
* A few rules receive an argument that must be kept intact because it can
* legitimately contain commas or colons: "regex" (a pattern) and "not"
* (a sub-rule that is itself parsed recursively). An empty argument string
* yields no extra arguments.
*
* @param string $name The rule name.
* @param string $raw The raw argument string as returned by parseRule().
*
* @return array The list of arguments to pass after the subject.
*/
private static function splitArguments(string $name, string $raw): array
{
if ($raw === '') {
return [];
}
$intactRules = ['regex', 'not'];
if (in_array($name, $intactRules, true)) {
return [$raw];
}
return explode(',', $raw);
}
/**