From 4d56f65bdf763d5baa5db2d8b9b2aa79856e964b Mon Sep 17 00:00:00 2001 From: chadicus Date: Thu, 14 Nov 2024 10:12:55 -0500 Subject: [PATCH 1/6] Add UuidFilter --- src/Filter/UuidFilter.php | 86 +++++++++++++++++++++++++ tests/Filter/UuidFilterTest.php | 107 ++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 src/Filter/UuidFilter.php create mode 100644 tests/Filter/UuidFilterTest.php diff --git a/src/Filter/UuidFilter.php b/src/Filter/UuidFilter.php new file mode 100644 index 0000000..6ba8939 --- /dev/null +++ b/src/Filter/UuidFilter.php @@ -0,0 +1,86 @@ + + */ +final class UuidFilterTest extends TestCase +{ + /** + * @var string + * @internal + */ + const UUID_V1 = '1a42403c-a29d-11ef-b864-0242ac120002'; + + /** + * @var string + * @internal + */ + const UUID_V4 = 'cc468b36-0b9d-4c93-b8e9-d5e949331ffb'; + + /** + * @var string + * @internal + */ + const UUID_V7 = '01932b4a-af2b-7093-af59-2fb2044d13d8'; + + /** + * @test + * @covers ::filter + */ + public function filterUuidV1() + { + $this->assertSame(self::UUID_V1, UuidFilter::filter(self::UUID_V1)); + } + + /** + * @test + * @covers ::filter + */ + public function filterUuidV4() + { + $this->assertSame(self::UUID_V4, UuidFilter::filter(self::UUID_V4)); + } + + /** + * @test + * @covers ::filter + */ + public function filterUuidV7() + { + $this->assertSame(self::UUID_V7, UuidFilter::filter(self::UUID_V7)); + } + + /** + * @test + * @covers ::filter + */ + public function filterNullAllowedNullIsTrue() + { + $this->assertNull(UuidFilter::filter(null, true)); + } + + /** + * @test + * @covers ::filter + */ + public function filterNullAllowedNullIsFalse() + { + $this->expectException(FilterException::class); + UuidFilter::filter(null, false); + } + + /** + * @test + * @covers ::filter + */ + public function filterWithInvalidVersionSpecified() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Filter does not support UUID v0'); + UuidFilter::filter(self::UUID_V7, false, [0]); + } + + /** + * @test + * @covers ::filter + */ + public function filterValueDoesNotMatchGivenVersions() + { + $this->expectException(FilterException::class); + $this->expectExceptionMessage( + sprintf( + UuidFilter::FILTER_ERROR_FORMAT, + self::UUID_V4, + implode(', ', [1,7]) + ) + ); + UuidFilter::filter(self::UUID_V4, false, [1,7]); + } +} From a13e9f64d09ba5cd70c836dec0c1f8b2cf6e7d93 Mon Sep 17 00:00:00 2001 From: chadicus Date: Thu, 14 Nov 2024 11:05:31 -0500 Subject: [PATCH 2/6] Add docs to README for UuidFilter --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index e8d127d..7e101e0 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,20 @@ $value = '{ "string": "value", "array": [1, 2, 3] }'; assert($value === ['string' => 'value', 'array' => [1, 2, 3]]); ``` +#### UuidFilter::filter + +This filter verifies a given string is a valid universally unique identifier. + +The second parameter can be set to `true` to allow null values through without an error. + +The third parameter determines which UUID version the value will be validated against. By default, the filter will succeed if the values matches version 1, 2, 3, 4, 5, 6, or 7 + +```php +$value = '1a42403c-a29d-11ef-b864-0242ac120002'; +$filtered = \TraderInteractive\Filter\UuidFilter::filter($value); +assert($value === $filtered); +``` + #### XmlFilter::filter This filter ensures the given string is valid XML. From 6aabfa8511262807cadee96de5787b1fb8d202dd Mon Sep 17 00:00:00 2001 From: chadicus Date: Thu, 14 Nov 2024 11:13:29 -0500 Subject: [PATCH 3/6] Fix code coverage in PhoneFilterTest --- tests/Filter/PhoneFilterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Filter/PhoneFilterTest.php b/tests/Filter/PhoneFilterTest.php index 48c0b1b..03407a7 100644 --- a/tests/Filter/PhoneFilterTest.php +++ b/tests/Filter/PhoneFilterTest.php @@ -92,7 +92,7 @@ public function filterWithAllowNull() * @param mixed $value The value to filter. * * @test - * @covers ::__invoke + * @covers ::filter * @dataProvider provideFilterThrowsException */ public function filterThrowsException($value) From 357f0dd969f9148aaf7b606ebe0a1fa6050250f4 Mon Sep 17 00:00:00 2001 From: chadicus Date: Thu, 14 Nov 2024 15:49:12 -0500 Subject: [PATCH 4/6] Use constants for all error messages --- src/Filter/UuidFilter.php | 14 ++++++++++++-- tests/Filter/UuidFilterTest.php | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Filter/UuidFilter.php b/src/Filter/UuidFilter.php index 6ba8939..102296d 100644 --- a/src/Filter/UuidFilter.php +++ b/src/Filter/UuidFilter.php @@ -22,6 +22,16 @@ final class UuidFilter */ const FILTER_ERROR_FORMAT = "Value '%s' is not a valid UUID. Versions checked (%s)"; + /** + * @var string + */ + const NULL_NOT_ALLOWED_ERROR = "Value is null, but null values are not allowed."; + + /** + * @var string + */ + const UNSUPPORTED_VERSION_ERROR_FORMAT = 'Filter does not support UUID v%d'; + /** * @var array * @internal @@ -69,7 +79,7 @@ public static function filter( private static function valueIsNullAndValid(bool $allowNull, string $value = null): bool { if ($allowNull === false && $value === null) { - throw new FilterException('Value failed filtering, $allowNull is set to false'); + throw new FilterException(self::NULL_NOT_ALLOWED_ERROR); } return $allowNull === true && $value === null; @@ -79,7 +89,7 @@ private static function validateVersions(array $versions) { foreach ($versions as $version) { if (!in_array($version, self::VALID_UUID_VERSIONS)) { - throw new InvalidArgumentException("Filter does not support UUID v{$version}"); + throw new InvalidArgumentException(sprintf(self::UNSUPPORTED_VERSION_ERROR_FORMAT, $version)); } } } diff --git a/tests/Filter/UuidFilterTest.php b/tests/Filter/UuidFilterTest.php index ed1319b..b618aef 100644 --- a/tests/Filter/UuidFilterTest.php +++ b/tests/Filter/UuidFilterTest.php @@ -84,7 +84,7 @@ public function filterNullAllowedNullIsFalse() public function filterWithInvalidVersionSpecified() { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Filter does not support UUID v0'); + $this->expectExceptionMessage(sprintf(UuidFilter::UNSUPPORTED_VERSION_ERROR_FORMAT, 0)); UuidFilter::filter(self::UUID_V7, false, [0]); } From 6304ab06f08516d06e30114b7f4bf4867132b788 Mon Sep 17 00:00:00 2001 From: chadicus Date: Thu, 14 Nov 2024 16:08:36 -0500 Subject: [PATCH 5/6] Allow nil UUIDs --- src/Filter/UuidFilter.php | 25 +++++++++++++++++++++++++ tests/Filter/UuidFilterTest.php | 26 ++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/Filter/UuidFilter.php b/src/Filter/UuidFilter.php index 102296d..3180a6b 100644 --- a/src/Filter/UuidFilter.php +++ b/src/Filter/UuidFilter.php @@ -22,6 +22,11 @@ final class UuidFilter */ const FILTER_ERROR_FORMAT = "Value '%s' is not a valid UUID. Versions checked (%s)"; + /** + * @var string + */ + const NIL_NOT_ALLOWED_ERROR_FORMAT = "Value '%s' is nil uuid, but nil values are not allowed."; + /** * @var string */ @@ -32,6 +37,11 @@ final class UuidFilter */ const UNSUPPORTED_VERSION_ERROR_FORMAT = 'Filter does not support UUID v%d'; + /** + * @var string + */ + const NIL_UUID = '00000000-0000-0000-0000-000000000000'; + /** * @var array * @internal @@ -44,6 +54,7 @@ final class UuidFilter * * @param string|null $value The value to be filtered. * @param bool $allowNull Flag to allow value to be null. + * @param bool $allowNil Flag to allow value to be a NIL UUID. * @param array $versions List of specific UUID version to validate against. * * @return string|null @@ -53,12 +64,17 @@ final class UuidFilter public static function filter( string $value = null, bool $allowNull = false, + bool $allowNil = false, array $versions = self::VALID_UUID_VERSIONS ) { if (self::valueIsNullAndValid($allowNull, $value)) { return null; } + if (self::valueIsNilAndValid($allowNil, $value)) { + return self::NIL_UUID; + } + self::validateVersions($versions); foreach ($versions as $version) { $pattern = sprintf(self::UUID_PATTERN_FORMAT, $version); @@ -85,6 +101,15 @@ private static function valueIsNullAndValid(bool $allowNull, string $value = nul return $allowNull === true && $value === null; } + private static function valueIsNilAndValid(bool $allowNil, string $value = null): bool + { + if ($allowNil === false && $value === self::NIL_UUID) { + throw new FilterException(sprintf(self::NIL_NOT_ALLOWED_ERROR_FORMAT, $value)); + } + + return $allowNil === true && $value === self::NIL_UUID; + } + private static function validateVersions(array $versions) { foreach ($versions as $version) { diff --git a/tests/Filter/UuidFilterTest.php b/tests/Filter/UuidFilterTest.php index b618aef..e105a2e 100644 --- a/tests/Filter/UuidFilterTest.php +++ b/tests/Filter/UuidFilterTest.php @@ -85,7 +85,7 @@ public function filterWithInvalidVersionSpecified() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage(sprintf(UuidFilter::UNSUPPORTED_VERSION_ERROR_FORMAT, 0)); - UuidFilter::filter(self::UUID_V7, false, [0]); + UuidFilter::filter(self::UUID_V7, false, false, [0]); } /** @@ -102,6 +102,28 @@ public function filterValueDoesNotMatchGivenVersions() implode(', ', [1,7]) ) ); - UuidFilter::filter(self::UUID_V4, false, [1,7]); + UuidFilter::filter(self::UUID_V4, false, false, [1,7]); + } + + /** + * @test + * @covers ::filter + */ + public function filterNilUuid() + { + $value = UuidFilter::NIL_UUID; + $this->assertSame($value, UuidFilter::filter($value, false, true)); + } + + /** + * @test + * @covers ::filter + */ + public function filterNilUuidNilNotAllowed() + { + $value = UuidFilter::NIL_UUID; + $this->expectException(FilterException::class); + $this->expectExceptionMessage(sprintf(UuidFilter::NIL_NOT_ALLOWED_ERROR_FORMAT, $value)); + UuidFilter::filter($value, false, false); } } From 140f12a7db39741e0a4635faf6836681412d2d87 Mon Sep 17 00:00:00 2001 From: chadicus Date: Thu, 14 Nov 2024 16:47:29 -0500 Subject: [PATCH 6/6] Update README with more UUID examples --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e101e0..b0cc51d 100644 --- a/README.md +++ b/README.md @@ -158,12 +158,30 @@ This filter verifies a given string is a valid universally unique identifier. The second parameter can be set to `true` to allow null values through without an error. -The third parameter determines which UUID version the value will be validated against. By default, the filter will succeed if the values matches version 1, 2, 3, 4, 5, 6, or 7 +The third parameter can be set to `true` to allow [Nil UUIDs](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.7) values through without an error. + +The fourth parameter determines which UUID version the value will be validated against. By default, the filter will succeed if the values matches version 1, 2, 3, 4, 5, 6, or 7 ```php +// Filtering an UUID string $value = '1a42403c-a29d-11ef-b864-0242ac120002'; $filtered = \TraderInteractive\Filter\UuidFilter::filter($value); assert($value === $filtered); + +// Filtering null values +$value = null; +$filtered = \TraderInteractive\Filter\UuidFilter::filter($value, true); +assert(null === $filtered); + +// Filtering a nil UUID +$value = '00000000-0000-0000-0000-000000000000'; +$filtered = \TraderInteractive\Filter\UuidFilter::filter($value, false, true); +assert($value === $filtered); + +// Filtering for only UUID v4 +$value = '1a42403c-a29d-41ef-b864-0242ac120002'; +$filtered = \TraderInteractive\Filter\UuidFilter::filter($value, false, false, [4]); +assert($value === $filtered); ``` #### XmlFilter::filter