From 569e93ac4e82410102d3431815cb65e701bef26a Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 30 Jun 2020 14:38:04 +0200 Subject: [PATCH] Re-implemented `Uuid::fromString()` so it produces a `LazyUuidFromString` when possible This should speed up `Uuid::fromString()` massively, leading to much shallower execution paths when `toString()` and similar simplistic API is required. --- src/Lazy/LazyUuidFromString.php | 409 +++++++++++++++++++++++ src/Rfc4122/Validator.php | 6 +- src/Uuid.php | 23 +- src/Validator/GenericValidator.php | 4 +- tests/Nonstandard/UuidV6Test.php | 7 +- tests/Rfc4122/ValidatorTest.php | 16 +- tests/UuidTest.php | 40 +-- tests/Validator/GenericValidatorTest.php | 14 +- 8 files changed, 482 insertions(+), 37 deletions(-) create mode 100644 src/Lazy/LazyUuidFromString.php diff --git a/src/Lazy/LazyUuidFromString.php b/src/Lazy/LazyUuidFromString.php new file mode 100644 index 0000000..031f0c9 --- /dev/null +++ b/src/Lazy/LazyUuidFromString.php @@ -0,0 +1,409 @@ + + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Uuid\Lazy; + +use DateTimeInterface; +use Ramsey\Uuid\Converter\NumberConverterInterface; +use Ramsey\Uuid\Exception\UnsupportedOperationException; +use Ramsey\Uuid\Fields\FieldsInterface; +use Ramsey\Uuid\Nonstandard\UuidV6; +use Ramsey\Uuid\Rfc4122\UuidV1; +use Ramsey\Uuid\Type\Hexadecimal; +use Ramsey\Uuid\Type\Integer as IntegerObject; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; +use function hex2bin; +use function str_replace; + +/** + * Lazy version of a UUID: its format has not been determined yet, so it is mostly only usable for string/bytes + * conversion. This object optimizes instantiation, serialization and string conversion time, at the cost of + * increased overhead for more advanced UUID operations. + * + * @psalm-immutable + * + * @internal this type is used internally for performance reasons, and is not supposed to be directly referenced + * in consumer libraries. + */ +final class LazyUuidFromString implements UuidInterface +{ + public const VALID_PATTERN = '/\A[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\z/ms'; + + /** @var non-empty-string */ + private $uuid; + + /** @var non-empty-string */ + public function __construct(string $uuid) + { + $this->uuid = $uuid; + } + + public function serialize(): string + { + return $this->uuid; + } + + public function unserialize($serialized): void + { + $this->uuid = $serialized; + } + + public function getNumberConverter(): NumberConverterInterface + { + return $this->unwrap() + ->getNumberConverter(); + } + + public function getFieldsHex(): array + { + return $this->unwrap() + ->getFieldsHex(); + } + + public function getClockSeqHiAndReservedHex(): string + { + return $this->unwrap() + ->getClockSeqHiAndReservedHex(); + } + + public function getClockSeqLowHex(): string + { + return $this->unwrap() + ->getClockSeqLowHex(); + } + + public function getClockSequenceHex(): string + { + return $this->unwrap() + ->getClockSequenceHex(); + } + + public function getDateTime(): DateTimeInterface + { + return $this->unwrap() + ->getDateTime(); + } + + public function getLeastSignificantBitsHex(): string + { + return $this->unwrap() + ->getLeastSignificantBitsHex(); + } + + public function getMostSignificantBitsHex(): string + { + return $this->unwrap() + ->getMostSignificantBitsHex(); + } + + public function getNodeHex(): string + { + return $this->unwrap() + ->getNodeHex(); + } + + public function getTimeHiAndVersionHex(): string + { + return $this->unwrap() + ->getTimeHiAndVersionHex(); + } + + public function getTimeLowHex(): string + { + return $this->unwrap() + ->getTimeLowHex(); + } + + public function getTimeMidHex(): string + { + return $this->unwrap() + ->getTimeMidHex(); + } + + public function getTimestampHex(): string + { + return $this->unwrap() + ->getTimestampHex(); + } + + public function getUrn(): string + { + return $this->unwrap() + ->getUrn(); + } + + public function getVariant(): ?int + { + return $this->unwrap() + ->getVariant(); + } + + public function getVersion(): ?int + { + return $this->unwrap() + ->getVersion(); + } + + public function compareTo(UuidInterface $other): int + { + return $this->unwrap() + ->compareTo($other); + } + + public function equals(?object $other): bool + { + if (! $other instanceof UuidInterface) { + return false; + } + + return $this->uuid === $other->toString(); + } + + public function getBytes(): string + { + return hex2bin(str_replace('-', '', $this->uuid)); + } + + public function getFields(): FieldsInterface + { + return $this->unwrap() + ->getFields(); + } + + public function getHex(): Hexadecimal + { + return $this->unwrap() + ->getHex(); + } + + public function getInteger(): IntegerObject + { + return $this->unwrap() + ->getInteger(); + } + + public function toString(): string + { + return $this->uuid; + } + + public function __toString(): string + { + return $this->uuid; + } + + public function jsonSerialize(): string + { + return $this->uuid; + } + + /** + * @deprecated Use {@see UuidInterface::getFields()} to get a + * {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface} + * instance, you may call {@see Rfc4122FieldsInterface::getClockSeqHiAndReserved()} + * and use the arbitrary-precision math library of your choice to + * convert it to a string integer. + */ + public function getClockSeqHiAndReserved(): string + { + $instance = $this->unwrap(); + + return $instance->getNumberConverter() + ->fromHex( + $instance->getFields() + ->getClockSeqHiAndReserved() + ->toString() + ); + } + + /** + * @deprecated Use {@see UuidInterface::getFields()} to get a + * {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface} + * instance, you may call {@see Rfc4122FieldsInterface::getClockSeqLow()} + * and use the arbitrary-precision math library of your choice to + * convert it to a string integer. + */ + public function getClockSeqLow(): string + { + $instance = $this->unwrap(); + + return $instance->getNumberConverter() + ->fromHex( + $instance->getFields() + ->getClockSeqLow() + ->toString() + ); + } + + /** + * @deprecated Use {@see UuidInterface::getFields()} to get a + * {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface} + * instance, you may call {@see Rfc4122FieldsInterface::getClockSeq()} + * and use the arbitrary-precision math library of your choice to + * convert it to a string integer. + */ + public function getClockSequence(): string + { + $instance = $this->unwrap(); + + return $instance->getNumberConverter() + ->fromHex( + $instance->getFields() + ->getClockSeq() + ->toString() + ); + } + + /** + * @deprecated This method will be removed in 5.0.0. There is no direct + * alternative, but the same information may be obtained by splitting + * in half the value returned by {@see UuidInterface::getHex()}. + */ + public function getLeastSignificantBits(): string + { + $instance = $this->unwrap(); + + return $instance->getNumberConverter() + ->fromHex(substr($instance->getHex()->toString(), 16)); + } + + /** + * @deprecated This method will be removed in 5.0.0. There is no direct + * alternative, but the same information may be obtained by splitting + * in half the value returned by {@see UuidInterface::getHex()}. + */ + public function getMostSignificantBits(): string + { + $instance = $this->unwrap(); + + return $instance->getNumberConverter() + ->fromHex(substr($instance->getHex()->toString(), 0, 16)); + } + + /** + * @deprecated Use {@see UuidInterface::getFields()} to get a + * {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface} + * instance, you may call {@see Rfc4122FieldsInterface::getNode()} + * and use the arbitrary-precision math library of your choice to + * convert it to a string integer. + */ + public function getNode(): string + { + $instance = $this->unwrap(); + + return $instance->getNumberConverter() + ->fromHex( + $instance->getFields() + ->getNode() + ->toString() + ); + } + + /** + * @deprecated Use {@see UuidInterface::getFields()} to get a + * {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface} + * instance, you may call {@see Rfc4122FieldsInterface::getTimeHiAndVersion()} + * and use the arbitrary-precision math library of your choice to + * convert it to a string integer. + */ + public function getTimeHiAndVersion(): string + { + $instance = $this->unwrap(); + + return $instance->getNumberConverter() + ->fromHex( + $instance->getFields() + ->getTimeHiAndVersion() + ->toString() + ); + } + + /** + * @deprecated Use {@see UuidInterface::getFields()} to get a + * {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface} + * instance, you may call {@see Rfc4122FieldsInterface::getTimeLow()} + * and use the arbitrary-precision math library of your choice to + * convert it to a string integer. + */ + public function getTimeLow(): string + { + $instance = $this->unwrap(); + + return $instance->getNumberConverter() + ->fromHex( + $instance->getFields() + ->getTimeLow() + ->toString() + ); + } + + /** + * @deprecated Use {@see UuidInterface::getFields()} to get a + * {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface} + * instance, you may call {@see Rfc4122FieldsInterface::getTimeMid()} + * and use the arbitrary-precision math library of your choice to + * convert it to a string integer. + */ + public function getTimeMid(): string + { + $instance = $this->unwrap(); + + return $instance->getNumberConverter() + ->fromHex( + $instance->getFields() + ->getTimeMid() + ->toString() + ); + } + + /** + * @deprecated Use {@see UuidInterface::getFields()} to get a + * {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface} + * instance, you may call {@see Rfc4122FieldsInterface::getTimestamp()} + * and use the arbitrary-precision math library of your choice to + * convert it to a string integer. + */ + public function getTimestamp(): string + { + $instance = $this->unwrap(); + $fields = $instance->getFields(); + + if ($fields->getVersion() !== 1) { + throw new UnsupportedOperationException('Not a time-based UUID'); + } + + return $instance->getNumberConverter() + ->fromHex($fields->getTimestamp()->toString()); + } + + public function toUuidV1(): UuidV1 + { + $instance = $this->unwrap(); + + if ($instance instanceof UuidV1) { + return $instance; + } + + assert($instance instanceof UuidV6); + + return $instance->toUuidV1(); + } + + private function unwrap(): UuidInterface + { + return Uuid::getFactory() + ->fromString($this->uuid); + } +} diff --git a/src/Rfc4122/Validator.php b/src/Rfc4122/Validator.php index 7dcf078..ed43c98 100644 --- a/src/Rfc4122/Validator.php +++ b/src/Rfc4122/Validator.php @@ -27,8 +27,8 @@ use function str_replace; */ final class Validator implements ValidatorInterface { - private const VALID_PATTERN = '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-' - . '[1-5]{1}[0-9A-Fa-f]{3}-[ABab89]{1}[0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$'; + private const VALID_PATTERN = '\A[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-' + . '[1-5]{1}[0-9A-Fa-f]{3}-[ABab89]{1}[0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}\z'; /** * @psalm-return non-empty-string @@ -44,6 +44,6 @@ final class Validator implements ValidatorInterface { $uuid = str_replace(['urn:', 'uuid:', 'URN:', 'UUID:', '{', '}'], '', $uuid); - return $uuid === Uuid::NIL || preg_match('/' . self::VALID_PATTERN . '/D', $uuid); + return $uuid === Uuid::NIL || preg_match('/' . self::VALID_PATTERN . '/Dms', $uuid); } } diff --git a/src/Uuid.php b/src/Uuid.php index f38e375..091348b 100644 --- a/src/Uuid.php +++ b/src/Uuid.php @@ -19,12 +19,16 @@ use Ramsey\Uuid\Codec\CodecInterface; use Ramsey\Uuid\Converter\NumberConverterInterface; use Ramsey\Uuid\Converter\TimeConverterInterface; use Ramsey\Uuid\Fields\FieldsInterface; +use Ramsey\Uuid\Lazy\LazyUuidFromString; use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface; use Ramsey\Uuid\Type\Hexadecimal; use Ramsey\Uuid\Type\Integer as IntegerObject; +use function bin2hex; +use function preg_match; use function str_replace; use function strcmp; +use function strtolower; /** * Uuid provides constants and static methods for working with and generating UUIDs @@ -194,6 +198,12 @@ class Uuid implements UuidInterface */ private static $factory = null; + /** + * @var bool flag to detect if the UUID factory was replaced internally, which disables all optimizations + * for the default/happy path internal scenarios + */ + private static $factoryReplaced = false; + /** * @var CodecInterface */ @@ -288,10 +298,10 @@ class Uuid implements UuidInterface { if (strlen($serialized) === 16) { /** @var Uuid $uuid */ - $uuid = self::fromBytes($serialized); + $uuid = self::getFactory()->fromBytes($serialized); } else { /** @var Uuid $uuid */ - $uuid = self::fromString($serialized); + $uuid = self::getFactory()->fromString($serialized); } $this->codec = $uuid->codec; @@ -375,6 +385,10 @@ class Uuid implements UuidInterface */ public static function setFactory(UuidFactoryInterface $factory): void { + // Note: non-string equality is intentional here. If the factory is configured differently, every assumption + // around purity is broken, and we have to internally decide everything differently. + self::$factoryReplaced = ($factory != new UuidFactory()); + self::$factory = $factory; } @@ -391,6 +405,7 @@ class Uuid implements UuidInterface */ public static function fromBytes(string $bytes): UuidInterface { + // @TODO optimize this endpoint too return self::getFactory()->fromBytes($bytes); } @@ -407,6 +422,10 @@ class Uuid implements UuidInterface */ public static function fromString(string $uuid): UuidInterface { + if (! self::$factoryReplaced && preg_match(LazyUuidFromString::VALID_PATTERN, $uuid) === 1) { + return new LazyUuidFromString(strtolower($uuid)); + } + return self::getFactory()->fromString($uuid); } diff --git a/src/Validator/GenericValidator.php b/src/Validator/GenericValidator.php index f6a6010..fd60955 100644 --- a/src/Validator/GenericValidator.php +++ b/src/Validator/GenericValidator.php @@ -29,7 +29,7 @@ final class GenericValidator implements ValidatorInterface /** * Regular expression pattern for matching a UUID of any variant. */ - private const VALID_PATTERN = '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$'; + private const VALID_PATTERN = '\A[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\z'; /** * @psalm-return non-empty-string @@ -45,6 +45,6 @@ final class GenericValidator implements ValidatorInterface { $uuid = str_replace(['urn:', 'uuid:', 'URN:', 'UUID:', '{', '}'], '', $uuid); - return $uuid === Uuid::NIL || preg_match('/' . self::VALID_PATTERN . '/D', $uuid); + return $uuid === Uuid::NIL || preg_match('/' . self::VALID_PATTERN . '/Dms', $uuid); } } diff --git a/tests/Nonstandard/UuidV6Test.php b/tests/Nonstandard/UuidV6Test.php index 3c0300e..899f032 100644 --- a/tests/Nonstandard/UuidV6Test.php +++ b/tests/Nonstandard/UuidV6Test.php @@ -11,9 +11,9 @@ use Ramsey\Uuid\Converter\NumberConverterInterface; use Ramsey\Uuid\Converter\TimeConverterInterface; use Ramsey\Uuid\Exception\DateTimeException; use Ramsey\Uuid\Exception\InvalidArgumentException; +use Ramsey\Uuid\Lazy\LazyUuidFromString; use Ramsey\Uuid\Nonstandard\UuidV6; use Ramsey\Uuid\Rfc4122\FieldsInterface; -use Ramsey\Uuid\Rfc4122\UuidV1; use Ramsey\Uuid\Test\TestCase; use Ramsey\Uuid\Type\Hexadecimal; use Ramsey\Uuid\Type\Time; @@ -123,8 +123,9 @@ class UuidV6Test extends TestCase */ public function testFromUuidV1(string $uuidv6, string $uuidv1): void { - /** @var UuidV1 $uuid1 */ - $uuid1 = Uuid::fromString($uuidv1); + /** @var LazyUuidFromString $uuid */ + $uuid = Uuid::fromString($uuidv1); + $uuid1 = $uuid->toUuidV1(); $uuid6 = UuidV6::fromUuidV1($uuid1); $this->assertSame($uuidv1, $uuid1->toString()); diff --git a/tests/Rfc4122/ValidatorTest.php b/tests/Rfc4122/ValidatorTest.php index 415e20f..e8b9770 100644 --- a/tests/Rfc4122/ValidatorTest.php +++ b/tests/Rfc4122/ValidatorTest.php @@ -75,13 +75,25 @@ class ValidatorTest extends TestCase 'value' => 'ff6f8cb0-c57da-51e1-9b21-0800200c9a66', 'expected' => false, ], + [ + 'value' => "ff6f8cb0-c57d-11e1-1b21-0800200c9a66\n", + 'expected' => false, + ], + [ + 'value' => "\nff6f8cb0-c57d-11e1-1b21-0800200c9a66", + 'expected' => false, + ], + [ + 'value' => "\nff6f8cb0-c57d-11e1-1b21-0800200c9a66\n", + 'expected' => false, + ], ]); } public function testGetPattern(): void { - $expectedPattern = '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-' - . '[1-5]{1}[0-9A-Fa-f]{3}-[ABab89]{1}[0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$'; + $expectedPattern = '\A[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-' + . '[1-5]{1}[0-9A-Fa-f]{3}-[ABab89]{1}[0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}\z'; $validator = new Validator(); diff --git a/tests/UuidTest.php b/tests/UuidTest.php index 5cbf6c7..7e8551b 100644 --- a/tests/UuidTest.php +++ b/tests/UuidTest.php @@ -68,9 +68,11 @@ class UuidTest extends TestCase public function testFromString(): void { - $uuid = Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66'); - $this->assertInstanceOf(Uuid::class, $uuid); - $this->assertEquals('ff6f8cb0-c57d-11e1-9b21-0800200c9a66', $uuid->toString()); + $this->assertEquals( + 'ff6f8cb0-c57d-11e1-9b21-0800200c9a66', + Uuid::fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66') + ->toString() + ); } /** @@ -109,6 +111,14 @@ class UuidTest extends TestCase Uuid::fromString('ff6f8cb0-c57d-11e1-9b21'); } + public function testFromStringWithLeadingNewLine(): void + { + $this->expectException(InvalidUuidStringException::class); + $this->expectExceptionMessage('Invalid UUID string:'); + + Uuid::fromString("\nd0d5f586-21d1-470c-8088-55c8857728dc"); + } + public function testFromStringWithTrailingNewLine(): void { $this->expectException(InvalidUuidStringException::class); @@ -713,8 +723,7 @@ class UuidTest extends TestCase $this->assertEquals(2, $uobj1->getVariant()); $this->assertEquals(3, $uobj1->getVersion()); - $this->assertEquals(Uuid::fromString($uuid), $uobj1); - $this->assertEquals((string) $uobj1, $uuid); + $this->assertEquals(Uuid::fromString($uuid)->toString(), $uobj1->toString()); $this->assertTrue($uobj1->equals($uobj2)); } @@ -848,8 +857,7 @@ class UuidTest extends TestCase $this->assertEquals(2, $uobj1->getVariant()); $this->assertEquals(5, $uobj1->getVersion()); - $this->assertEquals(Uuid::fromString($uuid), $uobj1); - $this->assertEquals((string) $uobj1, $uuid); + $this->assertEquals(Uuid::fromString($uuid)->toString(), $uobj1->toString()); $this->assertTrue($uobj1->equals($uobj2)); } @@ -1177,8 +1185,7 @@ class UuidTest extends TestCase string $time, string $clockSeq, int $variant, - ?int $version, - string $class + ?int $version ): void { $uuids = [ Uuid::fromString($string), @@ -1209,7 +1216,6 @@ class UuidTest extends TestCase $this->assertSame($clockSeq, $uuid->getClockSequenceHex()); $this->assertSame($variant, $uuid->getVariant()); $this->assertSame($version, $uuid->getVersion()); - $this->assertInstanceOf($class, $uuid); } } @@ -1242,7 +1248,6 @@ class UuidTest extends TestCase 'clock_seq' => '0000', 'variant' => Uuid::RESERVED_NCS, 'version' => null, - 'class' => NilUuid::class, ], [ 'string' => '00010203-0405-0607-0809-0a0b0c0d0e0f', @@ -1263,7 +1268,6 @@ class UuidTest extends TestCase 'clock_seq' => '0809', 'variant' => Uuid::RESERVED_NCS, 'version' => null, - 'class' => NonstandardUuid::class, ], [ 'string' => '02d9e6d5-9467-382e-8f9b-9300a64ac3cd', @@ -1284,7 +1288,6 @@ class UuidTest extends TestCase 'clock_seq' => '0f9b', 'variant' => Uuid::RFC_4122, 'version' => Uuid::UUID_TYPE_HASH_MD5, - 'class' => UuidV3::class, ], [ 'string' => '12345678-1234-5678-1234-567812345678', @@ -1305,7 +1308,6 @@ class UuidTest extends TestCase 'clock_seq' => '1234', 'variant' => Uuid::RESERVED_NCS, 'version' => null, - 'class' => NonstandardUuid::class, ], [ 'string' => '6ba7b810-9dad-11d1-80b4-00c04fd430c8', @@ -1326,7 +1328,6 @@ class UuidTest extends TestCase 'clock_seq' => '00b4', 'variant' => Uuid::RFC_4122, 'version' => Uuid::UUID_TYPE_TIME, - 'class' => UuidV1::class, ], [ 'string' => '6ba7b811-9dad-11d1-80b4-00c04fd430c8', @@ -1347,7 +1348,6 @@ class UuidTest extends TestCase 'clock_seq' => '00b4', 'variant' => Uuid::RFC_4122, 'version' => Uuid::UUID_TYPE_TIME, - 'class' => UuidV1::class, ], [ 'string' => '6ba7b812-9dad-11d1-80b4-00c04fd430c8', @@ -1368,7 +1368,6 @@ class UuidTest extends TestCase 'clock_seq' => '00b4', 'variant' => Uuid::RFC_4122, 'version' => Uuid::UUID_TYPE_TIME, - 'class' => UuidV1::class, ], [ 'string' => '6ba7b814-9dad-11d1-80b4-00c04fd430c8', @@ -1389,7 +1388,6 @@ class UuidTest extends TestCase 'clock_seq' => '00b4', 'variant' => Uuid::RFC_4122, 'version' => Uuid::UUID_TYPE_TIME, - 'class' => UuidV1::class, ], [ 'string' => '7d444840-9dc0-11d1-b245-5ffdce74fad2', @@ -1410,7 +1408,6 @@ class UuidTest extends TestCase 'clock_seq' => '3245', 'variant' => Uuid::RFC_4122, 'version' => Uuid::UUID_TYPE_TIME, - 'class' => UuidV1::class, ], [ 'string' => 'e902893a-9d22-3c7e-a7b8-d6e313b71d9f', @@ -1431,7 +1428,6 @@ class UuidTest extends TestCase 'clock_seq' => '27b8', 'variant' => Uuid::RFC_4122, 'version' => Uuid::UUID_TYPE_HASH_MD5, - 'class' => UuidV3::class, ], [ 'string' => 'eb424026-6f54-4ef8-a4d0-bb658a1fc6cf', @@ -1452,7 +1448,6 @@ class UuidTest extends TestCase 'clock_seq' => '24d0', 'variant' => Uuid::RFC_4122, 'version' => Uuid::UUID_TYPE_RANDOM, - 'class' => UuidV4::class, ], [ 'string' => 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6', @@ -1473,7 +1468,6 @@ class UuidTest extends TestCase 'clock_seq' => '2765', 'variant' => Uuid::RFC_4122, 'version' => Uuid::UUID_TYPE_TIME, - 'class' => UuidV1::class, ], [ 'string' => 'fffefdfc-fffe-fffe-fffe-fffefdfcfbfa', @@ -1494,7 +1488,6 @@ class UuidTest extends TestCase 'clock_seq' => '3ffe', 'variant' => Uuid::RESERVED_FUTURE, 'version' => null, - 'class' => NonstandardUuid::class, ], [ 'string' => 'ffffffff-ffff-ffff-ffff-ffffffffffff', @@ -1515,7 +1508,6 @@ class UuidTest extends TestCase 'clock_seq' => '3fff', 'variant' => Uuid::RESERVED_FUTURE, 'version' => null, - 'class' => NonstandardUuid::class, ], ]; } diff --git a/tests/Validator/GenericValidatorTest.php b/tests/Validator/GenericValidatorTest.php index 5a8699b..4a41330 100644 --- a/tests/Validator/GenericValidatorTest.php +++ b/tests/Validator/GenericValidatorTest.php @@ -72,12 +72,24 @@ class GenericValidatorTest extends TestCase 'value' => 'ff6f8cb0-c57da-51e1-9b21-0800200c9a66', 'expected' => false, ], + [ + 'value' => "ff6f8cb0-c57d-11e1-1b21-0800200c9a66\n", + 'expected' => false, + ], + [ + 'value' => "\nff6f8cb0-c57d-11e1-1b21-0800200c9a66", + 'expected' => false, + ], + [ + 'value' => "\nff6f8cb0-c57d-11e1-1b21-0800200c9a66\n", + 'expected' => false, + ], ]); } public function testGetPattern(): void { - $expectedPattern = '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$'; + $expectedPattern = '\A[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\z'; $validator = new GenericValidator();