fix(Validators): allow pipe in pattern regular expressions
The 'pattern:' argument of Validators::is() crashed on a valid regular
expression containing a pipe, e.g. Validators::is('a', 'pattern:(a|b)').
Two parsing bugs combined: the expression was split into validators on every
'|', tearing the regexp apart, and the pipe was also used as the PCRE
delimiter. A 'pattern:' argument now consumes the rest of the expression and
the delimiter is changed to '~', with the pattern anchored as a whole so bare
alternations work too. Pipe-as-OR semantics for combined validators are kept.
Fixes nette/utils#206
This commit is contained in:
1 parent
5361db9bd4
commit
6a4be2daab
2 files changed
+33
-8
No files matched your search
@@ -137,7 +137,9 @@ class Validators
|
|||||||
*/
|
*/
|
||||||
public static function is(mixed $value, string $expected): bool
|
public static function is(mixed $value, string $expected): bool
|
||||||
{
|
{
|
||||||
foreach (explode('|', $expected) as $item) {
|
$items = explode('|', $expected);
|
||||||
|
for ($i = 0, $count = count($items); $i < $count; $i++) {
|
||||||
|
$item = $items[$i];
|
||||||
if (str_ends_with($item, '[]')) {
|
if (str_ends_with($item, '[]')) {
|
||||||
if (is_iterable($value) && self::everyIs($value, substr($item, 0, -2))) {
|
if (is_iterable($value) && self::everyIs($value, substr($item, 0, -2))) {
|
||||||
return true;
|
return true;
|
||||||
@@ -152,7 +154,17 @@ class Validators
|
|||||||
}
|
}
|
||||||
|
|
||||||
[$type] = $item = explode(':', $item, 2);
|
[$type] = $item = explode(':', $item, 2);
|
||||||
if (isset(static::$validators[$type])) {
|
if ($type === 'pattern') {
|
||||||
|
// A regular expression may itself contain pipes, so the rest of the
|
||||||
|
// expression belongs to the pattern and must not be split into validators.
|
||||||
|
// The '~' delimiter is used so the pipe is not treated as a delimiter either.
|
||||||
|
$pattern = implode('|', array_merge(array_slice($item, 1), array_slice($items, $i + 1)));
|
||||||
|
if (Strings::match($value, '~^(?:' . $pattern . ')$~D')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
} elseif (isset(static::$validators[$type])) {
|
||||||
try {
|
try {
|
||||||
if (!static::$validators[$type]($value)) {
|
if (!static::$validators[$type]($value)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -160,12 +172,6 @@ class Validators
|
|||||||
} catch (\TypeError) {
|
} catch (\TypeError) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} elseif ($type === 'pattern') {
|
|
||||||
if (Strings::match($value, '|^' . ($item[1] ?? '') . '$|D')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
} elseif (!$value instanceof $type) {
|
} elseif (!$value instanceof $type) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,6 +259,25 @@ test('validates string against a regular expression pattern', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('allows pipe inside a regular expression pattern', function () {
|
||||||
|
// grouped alternation, https://github.com/nette/utils/issues/206
|
||||||
|
Assert::true(Validators::is('a', 'pattern:(a|b)'));
|
||||||
|
Assert::true(Validators::is('b', 'pattern:(a|b)'));
|
||||||
|
Assert::false(Validators::is('c', 'pattern:(a|b)'));
|
||||||
|
|
||||||
|
// bare alternation must no longer throw and is anchored as a whole
|
||||||
|
Assert::true(Validators::is('a', 'pattern:a|b'));
|
||||||
|
Assert::true(Validators::is('b', 'pattern:a|b'));
|
||||||
|
Assert::false(Validators::is('c', 'pattern:a|b'));
|
||||||
|
Assert::false(Validators::is('ab', 'pattern:a|b'));
|
||||||
|
|
||||||
|
// a pattern combined with other validators
|
||||||
|
Assert::true(Validators::is(5, 'int|pattern:(a|b)'));
|
||||||
|
Assert::true(Validators::is('a', 'int|pattern:(a|b)'));
|
||||||
|
Assert::false(Validators::is('c', 'int|pattern:(a|b)'));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
test('ensures alphanumeric string meets minimum length', function () {
|
test('ensures alphanumeric string meets minimum length', function () {
|
||||||
Assert::false(Validators::is('', 'alnum'));
|
Assert::false(Validators::is('', 'alnum'));
|
||||||
Assert::false(Validators::is('a-1', 'alnum'));
|
Assert::false(Validators::is('a-1', 'alnum'));
|
||||||
|
|||||||
Reference in new issue
Block a user