From 77c97f30963bcbe6a3286219b6252d7f0f34a8e9 Mon Sep 17 00:00:00 2001 From: Kristian Hempel Date: Thu, 17 Jul 2025 15:36:48 +0200 Subject: [PATCH 1/3] add regex argument to `string()` & `nullOrString()` --- src/Util/ValueObjectTrait.php | 24 +++++++--- tests/Unit/Util/ValueObjectTraitTest.php | 60 ++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/Util/ValueObjectTrait.php b/src/Util/ValueObjectTrait.php index 66c41f8..8bf6d5c 100644 --- a/src/Util/ValueObjectTrait.php +++ b/src/Util/ValueObjectTrait.php @@ -362,10 +362,11 @@ final protected static function nullOrMultiLink(array $values, string $key): ?Mu /** * Returns null if the value is not set or a trimmed non-empty string. * - * @param array $values - * @param non-empty-string $key + * @param array $values + * @param non-empty-string $key + * @param null|non-empty-string $regex */ - final protected static function nullOrString(array $values, string $key, ?int $maxLength = null): ?string + final protected static function nullOrString(array $values, string $key, ?int $maxLength = null, ?string $regex = null): ?string { if (!\array_key_exists($key, $values)) { return null; @@ -376,6 +377,11 @@ final protected static function nullOrString(array $values, string $key, ?int $m Assert::maxLength($values[$key], $maxLength); } + if (null !== $regex) { + Assert::stringNotEmpty($regex); + Assert::regex($values[$key], $regex); + } + try { return TrimmedNonEmptyString::from($values[$key])->toString(); } catch (\InvalidArgumentException) { @@ -386,10 +392,11 @@ final protected static function nullOrString(array $values, string $key, ?int $m /** * Returns a trimmed non-empty string. * - * @param array $values - * @param non-empty-string $key + * @param array $values + * @param non-empty-string $key + * @param null|non-empty-string $regex */ - final protected static function string(array $values, string $key, ?int $maxLength = null): string + final protected static function string(array $values, string $key, ?int $maxLength = null, ?string $regex = null): string { Assert::keyExists($values, $key); @@ -397,6 +404,11 @@ final protected static function string(array $values, string $key, ?int $maxLeng Assert::maxLength($values[$key], $maxLength); } + if (null !== $regex) { + Assert::stringNotEmpty($regex); + Assert::regex($values[$key], $regex); + } + return TrimmedNonEmptyString::fromString($values[$key])->toString(); } diff --git a/tests/Unit/Util/ValueObjectTraitTest.php b/tests/Unit/Util/ValueObjectTraitTest.php index f50d738..d1fa860 100644 --- a/tests/Unit/Util/ValueObjectTraitTest.php +++ b/tests/Unit/Util/ValueObjectTraitTest.php @@ -1113,6 +1113,36 @@ public function nullOrStringThrowsExceptionWhenMaxLengthIsLessThenOne(): void $class::nullOrString($values, 'key', 0); } + #[Test] + public function nullOrStringWithRegex(): void + { + $class = new class() { + use ValueObjectTrait { + ValueObjectTrait::nullOrString as public; + } + }; + + $values = ['key' => 'hello-world']; + + self::assertSame('hello-world', $class::nullOrString($values, 'key', regex: '/^hello-world$/')); + } + + #[Test] + public function nullOrStringThrowsExceptionWhenRegexEmpty(): void + { + $class = new class() { + use ValueObjectTrait { + ValueObjectTrait::nullOrString as public; + } + }; + + $this->expectException(\InvalidArgumentException::class); + + $values = ['key' => 'hello-world']; + + $class::nullOrString($values, 'key', regex: ''); + } + #[Test] public function string(): void { @@ -1177,6 +1207,36 @@ public function stringThrowsExceptionWhenMaxLengthExceeded(): void $class::string($values, 'key', 5); } + #[Test] + public function stringWithRegex(): void + { + $class = new class() { + use ValueObjectTrait { + ValueObjectTrait::string as public; + } + }; + + $values = ['key' => 'hello-world']; + + self::assertSame('hello-world', $class::string($values, 'key', regex: '/^hello-world$/')); + } + + #[Test] + public function stringThrowsExceptionWhenRegexEmpty(): void + { + $class = new class() { + use ValueObjectTrait { + ValueObjectTrait::string as public; + } + }; + + $this->expectException(\InvalidArgumentException::class); + + $values = ['key' => 'hello-world']; + + $class::string($values, 'key', regex: ''); + } + #[Test] public function nullOrEditable(): void { From efbc9427835dce6aefdf2add539987e7bf0715e8 Mon Sep 17 00:00:00 2001 From: Kristian Hempel Date: Thu, 17 Jul 2025 15:44:28 +0200 Subject: [PATCH 2/3] fix --- tests/Unit/Util/ValueObjectTraitTest.php | 36 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/tests/Unit/Util/ValueObjectTraitTest.php b/tests/Unit/Util/ValueObjectTraitTest.php index d1fa860..cfe2d57 100644 --- a/tests/Unit/Util/ValueObjectTraitTest.php +++ b/tests/Unit/Util/ValueObjectTraitTest.php @@ -1136,13 +1136,29 @@ public function nullOrStringThrowsExceptionWhenRegexEmpty(): void } }; - $this->expectException(\InvalidArgumentException::class); - $values = ['key' => 'hello-world']; + $this->expectException(\InvalidArgumentException::class); + $class::nullOrString($values, 'key', regex: ''); } + #[Test] + public function nullOrStringThrowsExceptionWhenRegexNotMatch(): void + { + $class = new class() { + use ValueObjectTrait { + ValueObjectTrait::nullOrString as public; + } + }; + + $values = ['key' => 'not-matching']; + + $this->expectException(\InvalidArgumentException::class); + + $class::nullOrString($values, 'key', regex: '/^hello-world$/'); + } + #[Test] public function string(): void { @@ -1237,6 +1253,22 @@ public function stringThrowsExceptionWhenRegexEmpty(): void $class::string($values, 'key', regex: ''); } + #[Test] + public function stringThrowsExceptionWhenRegexNotMatch(): void + { + $class = new class() { + use ValueObjectTrait { + ValueObjectTrait::string as public; + } + }; + + $values = ['key' => 'not-matching']; + + $this->expectException(\InvalidArgumentException::class); + + $class::string($values, 'key', regex: '/^hello-world$/'); + } + #[Test] public function nullOrEditable(): void { From e6882733fbc0f0b93f778b22979fb66208c106e1 Mon Sep 17 00:00:00 2001 From: Kristian Hempel Date: Thu, 17 Jul 2025 15:55:19 +0200 Subject: [PATCH 3/3] fix --- phpstan-baseline.neon | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index fea2d95..0b3c5ca 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -192,6 +192,18 @@ parameters: count: 1 path: tests/Unit/Util/ValueObjectTraitTest.php + - + message: '#^Parameter \$regex of static method class@anonymous/tests/Unit/Util/ValueObjectTraitTest\.php\:1133\:\:nullOrString\(\) expects non\-empty\-string\|null, '''' given\.$#' + identifier: argument.type + count: 1 + path: tests/Unit/Util/ValueObjectTraitTest.php + + - + message: '#^Parameter \$regex of static method class@anonymous/tests/Unit/Util/ValueObjectTraitTest\.php\:1243\:\:string\(\) expects non\-empty\-string\|null, '''' given\.$#' + identifier: argument.type + count: 1 + path: tests/Unit/Util/ValueObjectTraitTest.php + - message: '#^Unable to resolve the template type T in call to method static method class@anonymous/tests/Unit/Util/ValueObjectTraitTest\.php\:332\:\:enum\(\)$#' identifier: argument.templateType