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.
This commit is contained in:
Marco Pivetta
2020-06-30 14:38:04 +02:00
parent 8d42044a99
commit 569e93ac4e
8 changed files with 482 additions and 37 deletions
+409
View File
@@ -0,0 +1,409 @@
<?php
/**
* This file is part of the ramsey/uuid library
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
* @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);
}
}
+3 -3
View File
@@ -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);
}
}
+21 -2
View File
@@ -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);
}
+2 -2
View File
@@ -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);
}
}
+4 -3
View File
@@ -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());
+14 -2
View File
@@ -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();
+16 -24
View File
@@ -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,
],
];
}
+13 -1
View File
@@ -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();