From 32a3937e4e66bcac85dc7331f49333fe8e8acd77 Mon Sep 17 00:00:00 2001 From: Frank Dekker Date: Sun, 9 Aug 2026 10:09:01 +0200 Subject: [PATCH 1/5] Add `Assert::regex` and `Assert::notRegex` --- README.md | 2 ++ src/Assert.php | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b595a8b..a538f38 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ Fluent assertion methods, inspired by `webmozart/assert`: - `notStartsWith` - `endsWith` - `notEndsWith` +- `regex` +- `notRegex` - `boolean` - `true` - `false` diff --git a/src/Assert.php b/src/Assert.php index bafdc21..b248faa 100644 --- a/src/Assert.php +++ b/src/Assert.php @@ -85,7 +85,7 @@ public static function isArray(mixed $value, ?string $message = null): array * @template T * @phpstan-assert T&list $value * - * @param T $value + * @param T $value * * @return T&list */ @@ -333,6 +333,51 @@ public static function stringable(mixed $value, ?string $message = null): string return $value; } + /** + * Assert value is a string AND matches regex pattern + * @template T + * @phpstan-assert string $value + * + * @param T $value + * + * @return T&string + */ + public static function regex(mixed $value, string $pattern, ?string $message = null): string + { + self::string($value, $message); + if (preg_match($pattern, $value) !== 1) { + throw ExceptionFactory::createException('a regex match for `' . $pattern . '`', $value, $message); + } + + return $value; + } + + /** + * Assert value is a string AND matches regex pattern + * @template T + * @phpstan-assert string $value + * + * @param T $value + * + * @return T&string + */ + public static function notRegex(mixed $value, string $pattern, ?string $message = null): string + { + self::string($value, $message); + if (preg_match($pattern, $value) === 1) { + throw new RuntimeException( + sprintf( + 'Expecting value not to be a regex match for `%s`, `%s` was given%s', + $pattern, + $value, + $message === null ? '' : '. ' . $message + ) + ); + } + + return $value; + } + /** * Assert value is at least one of the given types * @template T From bd6bd47f66b874e7b931c286753f886bcc9308bd Mon Sep 17 00:00:00 2001 From: Frank Dekker Date: Sun, 9 Aug 2026 10:20:52 +0200 Subject: [PATCH 2/5] Add coverage --- tests/Unit/AssertTest.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Unit/AssertTest.php b/tests/Unit/AssertTest.php index ec62a2f..1b0e2af 100644 --- a/tests/Unit/AssertTest.php +++ b/tests/Unit/AssertTest.php @@ -164,6 +164,34 @@ public function testStringable(): void static::assertSame($stringable, Assert::stringable($stringable)); } + public function testRegex(): void + { + static::assertSame('string123', Assert::regex('string123', '/^string\d+$/')); + } + + public function testRegexFailure(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage( + 'Expecting value to be a regex match for `/^string\\d+$/`, `string (string)` was given. More context about failure.' + ); + Assert::regex('string', '/^string\d+$/', 'More context about failure.'); + } + + public function testNotRegex(): void + { + static::assertSame('string', Assert::notRegex('string', '/^string\d+$/')); + } + + public function testNotRegexFailure(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage( + 'Expecting value not to be a regex match for `/^string\\d+$/`, `string123` was given. More context about failure.' + ); + Assert::notRegex('string123', '/^string\d+$/', 'More context about failure.'); + } + #[TestWith([Assert::class])] #[TestWith(['DR\Utils\Assert'])] public function testClassString(string $value): void From f15046bd6294b36832f328c00beb0d37eaac96c0 Mon Sep 17 00:00:00 2001 From: Frank Dekker Date: Sun, 9 Aug 2026 10:25:19 +0200 Subject: [PATCH 3/5] Fix phpstan --- phpstan-baseline.neon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 20af096..38bf8c1 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -55,9 +55,9 @@ parameters: path: src/PHPStan/Extension/ArraysRenameKeyReturnExtension.php - - message: '#^Using nullsafe property access on non\-nullable type PhpParser\\Node\\ArrayItem\. Use \-\> instead\.$#' + message: '#^Using nullsafe property access on non\-nullable type mixed\. Use \-\> instead\.$#' identifier: nullsafe.neverNull - count: 2 + count: 1 path: src/PHPStan/Lib/TypeNarrower.php - From 47d49c146c7de9b59f7f252b2489d0d02784962f Mon Sep 17 00:00:00 2001 From: Frank Dekker Date: Sun, 9 Aug 2026 10:30:46 +0200 Subject: [PATCH 4/5] Assert preg_match result --- src/Assert.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Assert.php b/src/Assert.php index b248faa..1a11bd4 100644 --- a/src/Assert.php +++ b/src/Assert.php @@ -345,7 +345,8 @@ public static function stringable(mixed $value, ?string $message = null): string public static function regex(mixed $value, string $pattern, ?string $message = null): string { self::string($value, $message); - if (preg_match($pattern, $value) !== 1) { + $match = self::notFalse(preg_match($pattern, $value), $pattern . ' is not valid regex pattern'); + if ($match !== 1) { throw ExceptionFactory::createException('a regex match for `' . $pattern . '`', $value, $message); } @@ -353,7 +354,7 @@ public static function regex(mixed $value, string $pattern, ?string $message = n } /** - * Assert value is a string AND matches regex pattern + * Assert value is a string AND not matches regex pattern * @template T * @phpstan-assert string $value * @@ -364,7 +365,8 @@ public static function regex(mixed $value, string $pattern, ?string $message = n public static function notRegex(mixed $value, string $pattern, ?string $message = null): string { self::string($value, $message); - if (preg_match($pattern, $value) === 1) { + $match = self::notFalse(preg_match($pattern, $value), $pattern . ' is not valid regex pattern'); + if ($match === 1) { throw new RuntimeException( sprintf( 'Expecting value not to be a regex match for `%s`, `%s` was given%s', From e5269090b8f9caf7615eb3dfcbebf49305985f13 Mon Sep 17 00:00:00 2001 From: Frank Dekker Date: Tue, 11 Aug 2026 12:26:05 +0200 Subject: [PATCH 5/5] Rename method --- README.md | 4 ++-- src/Assert.php | 4 ++-- tests/Unit/AssertTest.php | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a538f38..826979d 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,8 @@ Fluent assertion methods, inspired by `webmozart/assert`: - `notStartsWith` - `endsWith` - `notEndsWith` -- `regex` -- `notRegex` +- `regexMatch` +- `notRegexMatch` - `boolean` - `true` - `false` diff --git a/src/Assert.php b/src/Assert.php index 1a11bd4..2139c03 100644 --- a/src/Assert.php +++ b/src/Assert.php @@ -342,7 +342,7 @@ public static function stringable(mixed $value, ?string $message = null): string * * @return T&string */ - public static function regex(mixed $value, string $pattern, ?string $message = null): string + public static function regexMatch(mixed $value, string $pattern, ?string $message = null): string { self::string($value, $message); $match = self::notFalse(preg_match($pattern, $value), $pattern . ' is not valid regex pattern'); @@ -362,7 +362,7 @@ public static function regex(mixed $value, string $pattern, ?string $message = n * * @return T&string */ - public static function notRegex(mixed $value, string $pattern, ?string $message = null): string + public static function notRegexMatch(mixed $value, string $pattern, ?string $message = null): string { self::string($value, $message); $match = self::notFalse(preg_match($pattern, $value), $pattern . ' is not valid regex pattern'); diff --git a/tests/Unit/AssertTest.php b/tests/Unit/AssertTest.php index 1b0e2af..c1c9ac1 100644 --- a/tests/Unit/AssertTest.php +++ b/tests/Unit/AssertTest.php @@ -166,7 +166,7 @@ public function testStringable(): void public function testRegex(): void { - static::assertSame('string123', Assert::regex('string123', '/^string\d+$/')); + static::assertSame('string123', Assert::regexMatch('string123', '/^string\d+$/')); } public function testRegexFailure(): void @@ -175,12 +175,12 @@ public function testRegexFailure(): void $this->expectExceptionMessage( 'Expecting value to be a regex match for `/^string\\d+$/`, `string (string)` was given. More context about failure.' ); - Assert::regex('string', '/^string\d+$/', 'More context about failure.'); + Assert::regexMatch('string', '/^string\d+$/', 'More context about failure.'); } public function testNotRegex(): void { - static::assertSame('string', Assert::notRegex('string', '/^string\d+$/')); + static::assertSame('string', Assert::notRegexMatch('string', '/^string\d+$/')); } public function testNotRegexFailure(): void @@ -189,7 +189,7 @@ public function testNotRegexFailure(): void $this->expectExceptionMessage( 'Expecting value not to be a regex match for `/^string\\d+$/`, `string123` was given. More context about failure.' ); - Assert::notRegex('string123', '/^string\d+$/', 'More context about failure.'); + Assert::notRegexMatch('string123', '/^string\d+$/', 'More context about failure.'); } #[TestWith([Assert::class])]