Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit c1e5142

Browse files
committed
feat: added Regex rule
1 parent 994b8d3 commit c1e5142

File tree

5 files changed

+130
-0
lines changed

5 files changed

+130
-0
lines changed

src/ChainedValidatorInterface.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ public function range(
9595
?string $message = null
9696
): ChainedValidatorInterface&Validator;
9797

98+
public function regex(
99+
string $pattern,
100+
bool $match = true,
101+
?callable $normalizer = null,
102+
?string $message = null
103+
): ChainedValidatorInterface&Validator;
104+
98105
public function rule(
99106
RuleInterface $constraint
100107
): ChainedValidatorInterface&Validator;

src/Exception/RegexException.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Exception;
4+
5+
class RegexException extends ValidationException {}

src/Rule/Regex.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Rule;
4+
5+
use ProgrammatorDev\Validator\Exception\RegexException;
6+
use ProgrammatorDev\Validator\Exception\UnexpectedTypeException;
7+
use ProgrammatorDev\Validator\Exception\UnexpectedValueException;
8+
9+
class Regex extends AbstractRule implements RuleInterface
10+
{
11+
/** @var ?callable */
12+
private $normalizer;
13+
private string $message = 'The {{ name }} value is not valid.';
14+
15+
public function __construct(
16+
private readonly string $pattern,
17+
private readonly bool $match = true,
18+
?callable $normalizer = null,
19+
?string $message = null
20+
)
21+
{
22+
$this->normalizer = $normalizer;
23+
$this->message = $message ?? $this->message;
24+
}
25+
26+
public function assert(mixed $value, ?string $name = null): void
27+
{
28+
if (!\is_scalar($value) && !$value instanceof \Stringable) {
29+
throw new UnexpectedTypeException('string|\Stringable', get_debug_type($value));
30+
}
31+
32+
$value = (string) $value;
33+
34+
if ($this->normalizer !== null) {
35+
$value = ($this->normalizer)($value);
36+
}
37+
38+
if (($regex = @\preg_match($this->pattern, $value)) === false) {
39+
throw new UnexpectedValueException('Invalid regular expression pattern.');
40+
}
41+
42+
if ($this->match xor $regex) {
43+
throw new RegexException(
44+
message: $this->message,
45+
parameters: [
46+
'name' => $name,
47+
'value' => $value,
48+
'pattern' => $this->pattern
49+
]
50+
);
51+
}
52+
}
53+
}

src/StaticValidatorInterface.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ public static function range(
9494
?string $message = null
9595
): ChainedValidatorInterface&Validator;
9696

97+
public static function regex(
98+
string $pattern,
99+
bool $match = true,
100+
?callable $normalizer = null,
101+
?string $message = null
102+
): ChainedValidatorInterface&Validator;
103+
97104
public static function rule(
98105
RuleInterface $constraint
99106
): ChainedValidatorInterface&Validator;

tests/RegexTest.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Test;
4+
5+
use ProgrammatorDev\Validator\Exception\RegexException;
6+
use ProgrammatorDev\Validator\Rule\Regex;
7+
use ProgrammatorDev\Validator\Test\Util\TestRuleFailureConditionTrait;
8+
use ProgrammatorDev\Validator\Test\Util\TestRuleMessageOptionTrait;
9+
use ProgrammatorDev\Validator\Test\Util\TestRuleSuccessConditionTrait;
10+
use ProgrammatorDev\Validator\Test\Util\TestRuleUnexpectedValueTrait;
11+
12+
class RegexTest extends AbstractTest
13+
{
14+
use TestRuleUnexpectedValueTrait;
15+
use TestRuleFailureConditionTrait;
16+
use TestRuleSuccessConditionTrait;
17+
use TestRuleMessageOptionTrait;
18+
19+
public static function provideRuleUnexpectedValueData(): \Generator
20+
{
21+
$unexpectedPatternMessage = '/Invalid regular expression pattern./';
22+
$unexpectedTypeMessage = '/Expected value of type "array|\Stringable", "(.*)" given./';
23+
24+
yield 'invalid pattern' => [new Regex('invalid'), 'abc', $unexpectedPatternMessage];
25+
yield 'invalid value type' => [new Regex('/[a-z]/'), ['abc'], $unexpectedTypeMessage];
26+
}
27+
28+
public static function provideRuleFailureConditionData(): \Generator
29+
{
30+
$value = 'abc';
31+
$exception = RegexException::class;
32+
$message = '/The (.*) value is not valid./';
33+
34+
yield 'match true' => [new Regex('/[0-9]/'), $value, $exception, $message];
35+
yield 'match false' => [new Regex('/[a-z]/', match: false), $value, $exception, $message];
36+
}
37+
38+
public static function provideRuleSuccessConditionData(): \Generator
39+
{
40+
$value = 'abc';
41+
42+
yield 'match true' => [new Regex('/[a-z]/'), $value];
43+
yield 'match false' => [new Regex('/[0-9]/', match: false), $value];
44+
yield 'normalizer' => [new Regex('/^\S*$/', normalizer: 'trim'), 'abc '];
45+
}
46+
47+
public static function provideRuleMessageOptionData(): \Generator
48+
{
49+
yield 'message' => [
50+
new Regex(
51+
pattern: '/[a-z]/',
52+
message: 'The {{ name }} value does not match the pattern {{ pattern }}.'
53+
),
54+
'123',
55+
'The test value does not match the pattern "/[a-z]/".'
56+
];
57+
}
58+
}

0 commit comments

Comments
 (0)