feature #39507 [Uid] Add UidFactory to create Ulid and Uuid from timestamps and randomness/nodes (fancyweb)
This PR was merged into the 5.3-dev branch.
Discussion
----------
[Uid] Add UidFactory to create Ulid and Uuid from timestamps and randomness/nodes
| Q | A
| ------------- | ---
| Branch? | 5.x
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | -
| License | MIT
| Doc PR | -
Ref https://github.com/symfony/symfony/pull/36097
When you migrate an existing resource identifier to an uid, you might want to choose the timestamp so that it is coherent with the creation date of the existing resource. (eg: I have a row in a table with id=1, created_at=2018-12-11 19:00:00, I would like to use that timestamp to create the resource Ulid).
I guess it can also be useful to choose the randomness of the Ulid or the node of the Uuid.
From what I understood, v3 and v5 don't need those features, this is why there are not in the factory.
See https://github.com/symfony/symfony/pull/39507#pullrequestreview-584904889 for more details.
Commits
-------
88a99ddbdf
[Uid] Add UuidFactory to create Ulid and Uuid from timestamps, namespaces and nodes
This commit is contained in:
commit
12b9d92b54
@ -6,6 +6,7 @@ CHANGELOG
|
||||
|
||||
* Deprecate `DoctrineTestHelper` and `TestRepositoryFactory`
|
||||
* [BC BREAK] Remove `UuidV*Generator` classes
|
||||
* Add `UuidGenerator`
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
@ -13,12 +13,24 @@ namespace Symfony\Bridge\Doctrine\IdGenerator;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Id\AbstractIdGenerator;
|
||||
use Symfony\Component\Uid\Factory\UlidFactory;
|
||||
use Symfony\Component\Uid\Ulid;
|
||||
|
||||
final class UlidGenerator extends AbstractIdGenerator
|
||||
{
|
||||
private $factory;
|
||||
|
||||
public function __construct(UlidFactory $factory = null)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
public function generate(EntityManager $em, $entity): Ulid
|
||||
{
|
||||
if ($this->factory) {
|
||||
return $this->factory->create();
|
||||
}
|
||||
|
||||
return new Ulid();
|
||||
}
|
||||
}
|
||||
|
82
src/Symfony/Bridge/Doctrine/IdGenerator/UuidGenerator.php
Normal file
82
src/Symfony/Bridge/Doctrine/IdGenerator/UuidGenerator.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\IdGenerator;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Id\AbstractIdGenerator;
|
||||
use Symfony\Component\Uid\Factory\UuidFactory;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
final class UuidGenerator extends AbstractIdGenerator
|
||||
{
|
||||
private $protoFactory;
|
||||
private $factory;
|
||||
private $entityGetter;
|
||||
|
||||
public function __construct(UuidFactory $factory = null)
|
||||
{
|
||||
$this->protoFactory = $this->factory = $factory ?? new UuidFactory();
|
||||
}
|
||||
|
||||
public function generate(EntityManager $em, $entity): Uuid
|
||||
{
|
||||
if (null !== $this->entityGetter) {
|
||||
if (\is_callable([$entity, $this->entityGetter])) {
|
||||
return $this->factory->create($entity->{$this->entityGetter}());
|
||||
}
|
||||
|
||||
return $this->factory->create($entity->{$this->entityGetter});
|
||||
}
|
||||
|
||||
return $this->factory->create();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Uuid|string|null $namespace
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function nameBased(string $entityGetter, $namespace = null): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->factory = $clone->protoFactory->nameBased($namespace);
|
||||
$clone->entityGetter = $entityGetter;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function randomBased(): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->factory = $clone->protoFactory->randomBased();
|
||||
$clone->entityGetter = null;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Uuid|string|null $node
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function timeBased($node = null): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->factory = $clone->protoFactory->timeBased($node);
|
||||
$clone->entityGetter = null;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ namespace Symfony\Bridge\Doctrine\Tests\IdGenerator;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UlidGenerator;
|
||||
use Symfony\Component\Uid\AbstractUid;
|
||||
use Symfony\Component\Uid\Factory\UlidFactory;
|
||||
use Symfony\Component\Uid\Ulid;
|
||||
|
||||
class UlidGeneratorTest extends TestCase
|
||||
@ -25,8 +25,23 @@ class UlidGeneratorTest extends TestCase
|
||||
$generator = new UlidGenerator();
|
||||
$ulid = $generator->generate($em, new Entity());
|
||||
|
||||
$this->assertInstanceOf(AbstractUid::class, $ulid);
|
||||
$this->assertInstanceOf(Ulid::class, $ulid);
|
||||
$this->assertTrue(Ulid::isValid($ulid));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires function \Symfony\Component\Uid\Factory\UlidFactory::create
|
||||
*/
|
||||
public function testUlidFactory()
|
||||
{
|
||||
$ulid = new Ulid('00000000000000000000000000');
|
||||
$em = new EntityManager();
|
||||
$factory = $this->createMock(UlidFactory::class);
|
||||
$factory->expects($this->any())
|
||||
->method('create')
|
||||
->willReturn($ulid);
|
||||
$generator = new UlidGenerator($factory);
|
||||
|
||||
$this->assertSame($ulid, $generator->generate($em, new Entity()));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\Tests\IdGenerator;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||
use Symfony\Component\Uid\Factory\UuidFactory;
|
||||
use Symfony\Component\Uid\NilUuid;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
use Symfony\Component\Uid\UuidV4;
|
||||
use Symfony\Component\Uid\UuidV6;
|
||||
|
||||
/**
|
||||
* @requires function \Symfony\Component\Uid\Factory\UuidFactory::create
|
||||
*/
|
||||
class UuidGeneratorTest extends TestCase
|
||||
{
|
||||
public function testUuidCanBeGenerated()
|
||||
{
|
||||
$em = new EntityManager();
|
||||
$generator = new UuidGenerator();
|
||||
$uuid = $generator->generate($em, new Entity());
|
||||
|
||||
$this->assertInstanceOf(Uuid::class, $uuid);
|
||||
}
|
||||
|
||||
public function testCustomUuidfactory()
|
||||
{
|
||||
$uuid = new NilUuid();
|
||||
$em = new EntityManager();
|
||||
$factory = $this->createMock(UuidFactory::class);
|
||||
$factory->expects($this->any())
|
||||
->method('create')
|
||||
->willReturn($uuid);
|
||||
$generator = new UuidGenerator($factory);
|
||||
|
||||
$this->assertSame($uuid, $generator->generate($em, new Entity()));
|
||||
}
|
||||
|
||||
public function testUuidfactory()
|
||||
{
|
||||
$em = new EntityManager();
|
||||
$generator = new UuidGenerator();
|
||||
$this->assertInstanceOf(UuidV6::class, $generator->generate($em, new Entity()));
|
||||
|
||||
$generator = $generator->randomBased();
|
||||
$this->assertInstanceOf(UuidV4::class, $generator->generate($em, new Entity()));
|
||||
|
||||
$generator = $generator->timeBased();
|
||||
$this->assertInstanceOf(UuidV6::class, $generator->generate($em, new Entity()));
|
||||
|
||||
$generator = $generator->nameBased('prop1', Uuid::NAMESPACE_OID);
|
||||
$this->assertEquals(Uuid::v5(new Uuid(Uuid::NAMESPACE_OID), '3'), $generator->generate($em, new Entity()));
|
||||
|
||||
$generator = $generator->nameBased('prop2', Uuid::NAMESPACE_OID);
|
||||
$this->assertEquals(Uuid::v5(new Uuid(Uuid::NAMESPACE_OID), '2'), $generator->generate($em, new Entity()));
|
||||
|
||||
$generator = $generator->nameBased('getProp4', Uuid::NAMESPACE_OID);
|
||||
$this->assertEquals(Uuid::v5(new Uuid(Uuid::NAMESPACE_OID), '4'), $generator->generate($em, new Entity()));
|
||||
|
||||
$factory = new UuidFactory(6, 6, 5, 5, null, Uuid::NAMESPACE_OID);
|
||||
$generator = new UuidGenerator($factory);
|
||||
$generator = $generator->nameBased('prop1');
|
||||
$this->assertEquals(Uuid::v5(new Uuid(Uuid::NAMESPACE_OID), '3'), $generator->generate($em, new Entity()));
|
||||
}
|
||||
}
|
||||
|
||||
class Entity
|
||||
{
|
||||
public $prop1 = 1;
|
||||
public $prop2 = 2;
|
||||
|
||||
public function prop1()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
public function getProp4()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ CHANGELOG
|
||||
* Added the `dispatcher` option to `debug:event-dispatcher`
|
||||
* Added the `event_dispatcher.dispatcher` tag
|
||||
* Added `assertResponseFormatSame()` in `BrowserKitAssertionsTrait`
|
||||
* Add support for configuring UUID factory services
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
@ -34,6 +34,7 @@ use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
|
||||
use Symfony\Component\RateLimiter\Policy\TokenBucketLimiter;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Translation\Translator;
|
||||
use Symfony\Component\Uid\Factory\UuidFactory;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
use Symfony\Component\WebLink\HttpHeaderSerializer;
|
||||
use Symfony\Component\Workflow\WorkflowEvents;
|
||||
@ -136,6 +137,7 @@ class Configuration implements ConfigurationInterface
|
||||
$this->addSecretsSection($rootNode);
|
||||
$this->addNotifierSection($rootNode);
|
||||
$this->addRateLimiterSection($rootNode);
|
||||
$this->addUidSection($rootNode);
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
@ -1891,4 +1893,37 @@ class Configuration implements ConfigurationInterface
|
||||
->end()
|
||||
;
|
||||
}
|
||||
|
||||
private function addUidSection(ArrayNodeDefinition $rootNode)
|
||||
{
|
||||
$rootNode
|
||||
->children()
|
||||
->arrayNode('uid')
|
||||
->info('Uid configuration')
|
||||
->{class_exists(UuidFactory::class) ? 'canBeDisabled' : 'canBeEnabled'}()
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->enumNode('default_uuid_version')
|
||||
->defaultValue(6)
|
||||
->values([6, 4, 1])
|
||||
->end()
|
||||
->enumNode('name_based_uuid_version')
|
||||
->defaultValue(5)
|
||||
->values([5, 3])
|
||||
->end()
|
||||
->scalarNode('name_based_uuid_namespace')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->enumNode('time_based_uuid_version')
|
||||
->defaultValue(6)
|
||||
->values([6, 1])
|
||||
->end()
|
||||
->scalarNode('time_based_uuid_node')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -160,6 +160,8 @@ use Symfony\Component\String\Slugger\SluggerInterface;
|
||||
use Symfony\Component\Translation\Command\XliffLintCommand as BaseXliffLintCommand;
|
||||
use Symfony\Component\Translation\PseudoLocalizationTranslator;
|
||||
use Symfony\Component\Translation\Translator;
|
||||
use Symfony\Component\Uid\Factory\UuidFactory;
|
||||
use Symfony\Component\Uid\UuidV4;
|
||||
use Symfony\Component\Validator\ConstraintValidatorInterface;
|
||||
use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader;
|
||||
use Symfony\Component\Validator\ObjectInitializerInterface;
|
||||
@ -449,6 +451,14 @@ class FrameworkExtension extends Extension
|
||||
$loader->load('web_link.php');
|
||||
}
|
||||
|
||||
if ($this->isConfigEnabled($container, $config['uid'])) {
|
||||
if (!class_exists(UuidFactory::class)) {
|
||||
throw new LogicException('Uid support cannot be enabled as the Uid component is not installed. Try running "composer require symfony/uid".');
|
||||
}
|
||||
|
||||
$this->registerUidConfiguration($config['uid'], $container, $loader);
|
||||
}
|
||||
|
||||
$this->addAnnotatedClassesToCompile([
|
||||
'**\\Controller\\',
|
||||
'**\\Entity\\',
|
||||
@ -2322,6 +2332,27 @@ class FrameworkExtension extends Extension
|
||||
$container->registerAliasForArgument($limiterId, RateLimiterFactory::class, $name.'.limiter');
|
||||
}
|
||||
|
||||
private function registerUidConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader)
|
||||
{
|
||||
$loader->load('uid.php');
|
||||
|
||||
$container->getDefinition('uuid.factory')
|
||||
->setArguments([
|
||||
$config['default_uuid_version'],
|
||||
$config['time_based_uuid_version'],
|
||||
$config['name_based_uuid_version'],
|
||||
UuidV4::class,
|
||||
$config['time_based_uuid_node'] ?? null,
|
||||
$config['name_based_uuid_namespace'] ?? null,
|
||||
])
|
||||
;
|
||||
|
||||
if (isset($config['name_based_uuid_namespace'])) {
|
||||
$container->getDefinition('name_based_uuid.factory')
|
||||
->setArguments([$config['name_based_uuid_namespace']]);
|
||||
}
|
||||
}
|
||||
|
||||
private function resolveTrustedHeaders(array $headers): int
|
||||
{
|
||||
$trustedHeaders = 0;
|
||||
|
@ -35,6 +35,7 @@
|
||||
<xsd:element name="mailer" type="mailer" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="http-cache" type="http_cache" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="rate-limiter" type="rate_limiter" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="uid" type="uid" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:choice>
|
||||
|
||||
<xsd:attribute name="http-method-override" type="xsd:boolean" />
|
||||
@ -692,4 +693,35 @@
|
||||
<xsd:attribute name="interval" type="xsd:string" />
|
||||
<xsd:attribute name="amount" type="xsd:int" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="uid">
|
||||
<xsd:attribute name="enabled" type="xsd:boolean" />
|
||||
<xsd:attribute name="default_uuid_version" type="default_uuid_version" />
|
||||
<xsd:attribute name="name_based_uuid_version" type="name_based_uuid_version" />
|
||||
<xsd:attribute name="time_based_uuid_version" type="time_based_uuid_version" />
|
||||
<xsd:attribute name="name_based_uuid_namespace" type="xsd:string" />
|
||||
<xsd:attribute name="time_based_uuid_node" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:simpleType name="default_uuid_version">
|
||||
<xsd:restriction base="xsd:int">
|
||||
<xsd:enumeration value="6" />
|
||||
<xsd:enumeration value="4" />
|
||||
<xsd:enumeration value="1" />
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="name_based_uuid_version">
|
||||
<xsd:restriction base="xsd:int">
|
||||
<xsd:enumeration value="5" />
|
||||
<xsd:enumeration value="3" />
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:simpleType name="time_based_uuid_version">
|
||||
<xsd:restriction base="xsd:int">
|
||||
<xsd:enumeration value="6" />
|
||||
<xsd:enumeration value="1" />
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:schema>
|
||||
|
41
src/Symfony/Bundle/FrameworkBundle/Resources/config/uid.php
Normal file
41
src/Symfony/Bundle/FrameworkBundle/Resources/config/uid.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
||||
|
||||
use Symfony\Component\Uid\Factory\NameBasedUuidFactory;
|
||||
use Symfony\Component\Uid\Factory\RandomBasedUuidFactory;
|
||||
use Symfony\Component\Uid\Factory\TimeBasedUuidFactory;
|
||||
use Symfony\Component\Uid\Factory\UlidFactory;
|
||||
use Symfony\Component\Uid\Factory\UuidFactory;
|
||||
|
||||
return static function (ContainerConfigurator $container) {
|
||||
$container->services()
|
||||
->set('ulid.factory', UlidFactory::class)
|
||||
->alias(UlidFactory::class, 'ulid.factory')
|
||||
|
||||
->set('uuid.factory', UuidFactory::class)
|
||||
->alias(UuidFactory::class, 'uuid.factory')
|
||||
|
||||
->set('name_based_uuid.factory', NameBasedUuidFactory::class)
|
||||
->factory([service('uuid.factory'), 'nameBased'])
|
||||
->args([abstract_arg('Please set the "framework.uid.name_based_uuid_namespace" configuration option to use the "name_based_uuid.factory" service')])
|
||||
->alias(NameBasedUuidFactory::class, 'name_based_uuid.factory')
|
||||
|
||||
->set('random_based_uuid.factory', RandomBasedUuidFactory::class)
|
||||
->factory([service('uuid.factory'), 'randomBased'])
|
||||
->alias(RandomBasedUuidFactory::class, 'random_based_uuid.factory')
|
||||
|
||||
->set('time_based_uuid.factory', TimeBasedUuidFactory::class)
|
||||
->factory([service('uuid.factory'), 'timeBased'])
|
||||
->alias(TimeBasedUuidFactory::class, 'time_based_uuid.factory')
|
||||
;
|
||||
};
|
@ -22,6 +22,7 @@ use Symfony\Component\Lock\Store\SemaphoreStore;
|
||||
use Symfony\Component\Mailer\Mailer;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Notifier\Notifier;
|
||||
use Symfony\Component\Uid\Factory\UuidFactory;
|
||||
|
||||
class ConfigurationTest extends TestCase
|
||||
{
|
||||
@ -563,6 +564,12 @@ class ConfigurationTest extends TestCase
|
||||
'enabled' => false,
|
||||
'limiters' => [],
|
||||
],
|
||||
'uid' => [
|
||||
'enabled' => class_exists(UuidFactory::class),
|
||||
'default_uuid_version' => 6,
|
||||
'name_based_uuid_version' => 5,
|
||||
'time_based_uuid_version' => 6,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ class BinaryUtil
|
||||
/**
|
||||
* @param string $time Count of 100-nanosecond intervals since the UUID epoch 1582-10-15 00:00:00 in hexadecimal
|
||||
*/
|
||||
public static function timeToDateTime(string $time): \DateTimeImmutable
|
||||
public static function hexToDateTime(string $time): \DateTimeImmutable
|
||||
{
|
||||
if (\PHP_INT_SIZE >= 8) {
|
||||
$time = (string) (hexdec($time) - self::TIME_OFFSET_INT);
|
||||
@ -142,4 +142,34 @@ class BinaryUtil
|
||||
|
||||
return \DateTimeImmutable::createFromFormat('U.u?', substr_replace($time, '.', -7, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Count of 100-nanosecond intervals since the UUID epoch 1582-10-15 00:00:00 in hexadecimal
|
||||
*/
|
||||
public static function dateTimeToHex(\DateTimeInterface $time): string
|
||||
{
|
||||
if (\PHP_INT_SIZE >= 8) {
|
||||
if (-self::TIME_OFFSET_INT > $time = (int) $time->format('Uu0')) {
|
||||
throw new \InvalidArgumentException('The given UUID date cannot be earlier than 1582-10-15.');
|
||||
}
|
||||
|
||||
return str_pad(dechex(self::TIME_OFFSET_INT + $time), 16, '0', \STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
$time = $time->format('Uu0');
|
||||
$negative = '-' === $time[0];
|
||||
if ($negative && self::TIME_OFFSET_INT < $time = substr($time, 1)) {
|
||||
throw new \InvalidArgumentException('The given UUID date cannot be earlier than 1582-10-15.');
|
||||
}
|
||||
$time = self::fromBase($time, self::BASE10);
|
||||
$time = str_pad($time, 8, "\0", \STR_PAD_LEFT);
|
||||
|
||||
if ($negative) {
|
||||
$time = self::add($time, self::TIME_OFFSET_COM1) ^ "\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
} else {
|
||||
$time = self::add($time, self::TIME_OFFSET_BIN);
|
||||
}
|
||||
|
||||
return bin2hex($time);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ CHANGELOG
|
||||
* Add `AbstractUid::fromBinary()`, `AbstractUid::fromBase58()`, `AbstractUid::fromBase32()` and `AbstractUid::fromRfc4122()`
|
||||
* [BC BREAK] Replace `UuidV1::getTime()`, `UuidV6::getTime()` and `Ulid::getTime()` by `UuidV1::getDateTime()`, `UuidV6::getDateTime()` and `Ulid::getDateTime()`
|
||||
* Add `Uuid::NAMESPACE_*` constants from RFC4122
|
||||
* Add `UlidFactory`, `UuidFactory`, `RandomBasedUuidFactory`, `TimeBasedUuidFactory` and `NameBasedUuidFactory`
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
47
src/Symfony/Component/Uid/Factory/NameBasedUuidFactory.php
Normal file
47
src/Symfony/Component/Uid/Factory/NameBasedUuidFactory.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Uid\Factory;
|
||||
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
use Symfony\Component\Uid\UuidV3;
|
||||
use Symfony\Component\Uid\UuidV5;
|
||||
|
||||
class NameBasedUuidFactory
|
||||
{
|
||||
private $class;
|
||||
private $namespace;
|
||||
|
||||
public function __construct(string $class, Uuid $namespace)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UuidV5|UuidV3
|
||||
*/
|
||||
public function create(string $name): Uuid
|
||||
{
|
||||
switch ($class = $this->class) {
|
||||
case UuidV5::class: return Uuid::v5($this->namespace, $name);
|
||||
case UuidV3::class: return Uuid::v3($this->namespace, $name);
|
||||
}
|
||||
|
||||
if (is_subclass_of($class, UuidV5::class)) {
|
||||
$uuid = Uuid::v5($this->namespace, $name);
|
||||
} else {
|
||||
$uuid = Uuid::v3($this->namespace, $name);
|
||||
}
|
||||
|
||||
return new $class($uuid);
|
||||
}
|
||||
}
|
31
src/Symfony/Component/Uid/Factory/RandomBasedUuidFactory.php
Normal file
31
src/Symfony/Component/Uid/Factory/RandomBasedUuidFactory.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Uid\Factory;
|
||||
|
||||
use Symfony\Component\Uid\UuidV4;
|
||||
|
||||
class RandomBasedUuidFactory
|
||||
{
|
||||
private $class;
|
||||
|
||||
public function __construct(string $class)
|
||||
{
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public function create(): UuidV4
|
||||
{
|
||||
$class = $this->class;
|
||||
|
||||
return new $class();
|
||||
}
|
||||
}
|
42
src/Symfony/Component/Uid/Factory/TimeBasedUuidFactory.php
Normal file
42
src/Symfony/Component/Uid/Factory/TimeBasedUuidFactory.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Uid\Factory;
|
||||
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
use Symfony\Component\Uid\UuidV1;
|
||||
use Symfony\Component\Uid\UuidV6;
|
||||
|
||||
class TimeBasedUuidFactory
|
||||
{
|
||||
private $class;
|
||||
private $node;
|
||||
|
||||
public function __construct(string $class, Uuid $node = null)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UuidV6|UuidV1
|
||||
*/
|
||||
public function create(\DateTimeInterface $time = null): Uuid
|
||||
{
|
||||
$class = $this->class;
|
||||
|
||||
if (null === $time && null === $this->node) {
|
||||
return new $class();
|
||||
}
|
||||
|
||||
return new $class($class::generate($time, $this->node));
|
||||
}
|
||||
}
|
22
src/Symfony/Component/Uid/Factory/UlidFactory.php
Normal file
22
src/Symfony/Component/Uid/Factory/UlidFactory.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Uid\Factory;
|
||||
|
||||
use Symfony\Component\Uid\Ulid;
|
||||
|
||||
class UlidFactory
|
||||
{
|
||||
public function create(\DateTimeInterface $time = null): Ulid
|
||||
{
|
||||
return new Ulid(null === $time ? null : Ulid::generate($time));
|
||||
}
|
||||
}
|
104
src/Symfony/Component/Uid/Factory/UuidFactory.php
Normal file
104
src/Symfony/Component/Uid/Factory/UuidFactory.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Uid\Factory;
|
||||
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
use Symfony\Component\Uid\UuidV1;
|
||||
use Symfony\Component\Uid\UuidV4;
|
||||
use Symfony\Component\Uid\UuidV5;
|
||||
use Symfony\Component\Uid\UuidV6;
|
||||
|
||||
class UuidFactory
|
||||
{
|
||||
private $defaultClass;
|
||||
private $timeBasedClass;
|
||||
private $nameBasedClass;
|
||||
private $randomBasedClass;
|
||||
private $timeBasedNode;
|
||||
private $nameBasedNamespace;
|
||||
|
||||
/**
|
||||
* @param string|int $defaultClass
|
||||
* @param string|int $timeBasedClass
|
||||
* @param string|int $nameBasedClass
|
||||
* @param string|int $randomBasedClass
|
||||
* @param Uuid|string|null $timeBasedNode
|
||||
* @param Uuid|string|null $nameBasedNamespace
|
||||
*/
|
||||
public function __construct($defaultClass = UuidV6::class, $timeBasedClass = UuidV6::class, $nameBasedClass = UuidV5::class, $randomBasedClass = UuidV4::class, $timeBasedNode = null, $nameBasedNamespace = null)
|
||||
{
|
||||
if (null !== $timeBasedNode && !$timeBasedNode instanceof Uuid) {
|
||||
$timeBasedNode = Uuid::fromString($timeBasedNode);
|
||||
}
|
||||
|
||||
if (null !== $nameBasedNamespace && !$nameBasedNamespace instanceof Uuid) {
|
||||
$nameBasedNamespace = Uuid::fromString($nameBasedNamespace);
|
||||
}
|
||||
|
||||
$this->defaultClass = is_numeric($defaultClass) ? Uuid::class.'V'.$defaultClass : $defaultClass;
|
||||
$this->timeBasedClass = is_numeric($timeBasedClass) ? Uuid::class.'V'.$timeBasedClass : $timeBasedClass;
|
||||
$this->nameBasedClass = is_numeric($nameBasedClass) ? Uuid::class.'V'.$nameBasedClass : $nameBasedClass;
|
||||
$this->randomBasedClass = is_numeric($randomBasedClass) ? Uuid::class.'V'.$randomBasedClass : $randomBasedClass;
|
||||
$this->timeBasedNode = $timeBasedNode;
|
||||
$this->nameBasedNamespace = $nameBasedNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UuidV6|UuidV4|UuidV1
|
||||
*/
|
||||
public function create(): Uuid
|
||||
{
|
||||
$class = $this->defaultClass;
|
||||
|
||||
return new $class();
|
||||
}
|
||||
|
||||
public function randomBased(): RandomBasedUuidFactory
|
||||
{
|
||||
return new RandomBasedUuidFactory($this->randomBasedClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Uuid|string|null $node
|
||||
*/
|
||||
public function timeBased($node = null): TimeBasedUuidFactory
|
||||
{
|
||||
$node ?? $node = $this->timeBasedNode;
|
||||
|
||||
if (null === $node) {
|
||||
$class = $this->timeBasedClass;
|
||||
$node = $this->timeBasedNode = new $class();
|
||||
} elseif (!$node instanceof Uuid) {
|
||||
$node = Uuid::fromString($node);
|
||||
}
|
||||
|
||||
return new TimeBasedUuidFactory($this->timeBasedClass, $node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Uuid|string|null $namespace
|
||||
*/
|
||||
public function nameBased($namespace = null): NameBasedUuidFactory
|
||||
{
|
||||
$namespace ?? $namespace = $this->nameBasedNamespace;
|
||||
|
||||
if (null === $namespace) {
|
||||
throw new \LogicException(sprintf('A namespace should be defined when using "%s()".', __METHOD__));
|
||||
}
|
||||
|
||||
if (!$namespace instanceof Uuid) {
|
||||
$namespace = Uuid::fromString($namespace);
|
||||
}
|
||||
|
||||
return new NameBasedUuidFactory($this->nameBasedClass, $namespace);
|
||||
}
|
||||
}
|
44
src/Symfony/Component/Uid/Tests/Factory/UlidFactoryTest.php
Normal file
44
src/Symfony/Component/Uid/Tests/Factory/UlidFactoryTest.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Uid\Tests\Factory;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Uid\Factory\UlidFactory;
|
||||
|
||||
final class UlidFactoryTest extends TestCase
|
||||
{
|
||||
public function testCreate()
|
||||
{
|
||||
$ulidFactory = new UlidFactory();
|
||||
|
||||
$ulidFactory->create();
|
||||
|
||||
$ulid1 = $ulidFactory->create(new \DateTime('@999999.123000'));
|
||||
$this->assertSame('999999.123000', $ulid1->getDateTime()->format('U.u'));
|
||||
$ulid2 = $ulidFactory->create(new \DateTime('@999999.123000'));
|
||||
$this->assertSame('999999.123000', $ulid2->getDateTime()->format('U.u'));
|
||||
|
||||
$this->assertFalse($ulid1->equals($ulid2));
|
||||
$this->assertSame(-1, ($ulid1->compare($ulid2)));
|
||||
|
||||
$ulid3 = $ulidFactory->create(new \DateTime('@1234.162524'));
|
||||
$this->assertSame('1234.162000', $ulid3->getDateTime()->format('U.u'));
|
||||
}
|
||||
|
||||
public function testCreateWithInvalidTimestamp()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('The timestamp must be positive.');
|
||||
|
||||
(new UlidFactory())->create(new \DateTime('@-1000'));
|
||||
}
|
||||
}
|
93
src/Symfony/Component/Uid/Tests/Factory/UuidFactoryTest.php
Normal file
93
src/Symfony/Component/Uid/Tests/Factory/UuidFactoryTest.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Uid\Tests\Factory;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Uid\Factory\UuidFactory;
|
||||
use Symfony\Component\Uid\NilUuid;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
use Symfony\Component\Uid\UuidV1;
|
||||
use Symfony\Component\Uid\UuidV3;
|
||||
use Symfony\Component\Uid\UuidV4;
|
||||
use Symfony\Component\Uid\UuidV5;
|
||||
use Symfony\Component\Uid\UuidV6;
|
||||
|
||||
final class UuidFactoryTest extends TestCase
|
||||
{
|
||||
public function testCreateNamedDefaultVersion()
|
||||
{
|
||||
$this->assertInstanceOf(UuidV5::class, (new UuidFactory())->nameBased('6f80c216-0492-4421-bd82-c10ab929ae84')->create('foo'));
|
||||
$this->assertInstanceOf(UuidV3::class, (new UuidFactory(6, 6, 3))->nameBased('6f80c216-0492-4421-bd82-c10ab929ae84')->create('foo'));
|
||||
}
|
||||
|
||||
public function testCreateNamed()
|
||||
{
|
||||
$uuidFactory = new UuidFactory();
|
||||
|
||||
// Test custom namespace
|
||||
$uuid1 = $uuidFactory->nameBased('6f80c216-0492-4421-bd82-c10ab929ae84')->create('foo');
|
||||
$this->assertInstanceOf(UuidV5::class, $uuid1);
|
||||
$this->assertSame('d521ceb7-3e31-5954-b873-92992c697ab9', (string) $uuid1);
|
||||
|
||||
// Test default namespace override
|
||||
$uuid2 = $uuidFactory->nameBased(Uuid::v4())->create('foo');
|
||||
$this->assertFalse($uuid1->equals($uuid2));
|
||||
|
||||
// Test version override
|
||||
$uuidFactory = new UuidFactory(6, 6, 3, 4, new NilUuid(), '6f80c216-0492-4421-bd82-c10ab929ae84');
|
||||
$uuid3 = $uuidFactory->nameBased()->create('foo');
|
||||
$this->assertInstanceOf(UuidV3::class, $uuid3);
|
||||
}
|
||||
|
||||
public function testCreateTimedDefaultVersion()
|
||||
{
|
||||
$this->assertInstanceOf(UuidV6::class, (new UuidFactory())->timeBased()->create());
|
||||
$this->assertInstanceOf(UuidV1::class, (new UuidFactory(6, 1))->timeBased()->create());
|
||||
}
|
||||
|
||||
public function testCreateTimed()
|
||||
{
|
||||
$uuidFactory = new UuidFactory(6, 6, 5, 4, '6f80c216-0492-4421-bd82-c10ab929ae84');
|
||||
|
||||
// Test custom timestamp
|
||||
$uuid1 = $uuidFactory->timeBased()->create(new \DateTime('@1611076938.057800'));
|
||||
$this->assertInstanceOf(UuidV6::class, $uuid1);
|
||||
$this->assertSame('1611076938.057800', $uuid1->getDateTime()->format('U.u'));
|
||||
$this->assertSame('c10ab929ae84', $uuid1->getNode());
|
||||
|
||||
// Test default node override
|
||||
$uuid2 = $uuidFactory->timeBased('7c1ede70-3586-48ed-a984-23c8018d9174')->create();
|
||||
$this->assertInstanceOf(UuidV6::class, $uuid2);
|
||||
$this->assertSame('23c8018d9174', $uuid2->getNode());
|
||||
|
||||
// Test version override
|
||||
$uuid3 = (new UuidFactory(6, 1))->timeBased()->create();
|
||||
$this->assertInstanceOf(UuidV1::class, $uuid3);
|
||||
|
||||
// Test negative timestamp and round
|
||||
$uuid4 = $uuidFactory->timeBased()->create(new \DateTime('@-12219292800'));
|
||||
$this->assertSame('-12219292800.000000', $uuid4->getDateTime()->format('U.u'));
|
||||
}
|
||||
|
||||
public function testInvalidCreateTimed()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('The given UUID date cannot be earlier than 1582-10-15.');
|
||||
|
||||
(new UuidFactory())->timeBased()->create(new \DateTime('@-12219292800.001000'));
|
||||
}
|
||||
|
||||
public function testCreateRandom()
|
||||
{
|
||||
$this->assertInstanceOf(UuidV4::class, (new UuidFactory())->randomBased()->create());
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ class Ulid extends AbstractUid
|
||||
public function __construct(string $ulid = null)
|
||||
{
|
||||
if (null === $ulid) {
|
||||
$this->uid = self::generate();
|
||||
$this->uid = static::generate();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -124,10 +124,25 @@ class Ulid extends AbstractUid
|
||||
return \DateTimeImmutable::createFromFormat('U.u', substr_replace($time, '.', -3, 0));
|
||||
}
|
||||
|
||||
private static function generate(): string
|
||||
public static function generate(\DateTimeInterface $time = null): string
|
||||
{
|
||||
$time = microtime(false);
|
||||
$time = substr($time, 11).substr($time, 2, 3);
|
||||
if (null === $time) {
|
||||
return self::doGenerate();
|
||||
}
|
||||
|
||||
if (0 > $time = substr($time->format('Uu'), 0, -3)) {
|
||||
throw new \InvalidArgumentException('The timestamp must be positive.');
|
||||
}
|
||||
|
||||
return self::doGenerate($time);
|
||||
}
|
||||
|
||||
private static function doGenerate(string $mtime = null): string
|
||||
{
|
||||
if (null === $time = $mtime) {
|
||||
$time = microtime(false);
|
||||
$time = substr($time, 11).substr($time, 2, 3);
|
||||
}
|
||||
|
||||
if ($time !== self::$time) {
|
||||
$r = unpack('nr1/nr2/nr3/nr4/nr', random_bytes(10));
|
||||
@ -139,9 +154,13 @@ class Ulid extends AbstractUid
|
||||
self::$rand = array_values($r);
|
||||
self::$time = $time;
|
||||
} elseif ([0xFFFFF, 0xFFFFF, 0xFFFFF, 0xFFFFF] === self::$rand) {
|
||||
usleep(100);
|
||||
if (null === $mtime) {
|
||||
usleep(100);
|
||||
} else {
|
||||
self::$rand = [0, 0, 0, 0];
|
||||
}
|
||||
|
||||
return self::generate();
|
||||
return self::doGenerate($mtime);
|
||||
} else {
|
||||
for ($i = 3; $i >= 0 && 0xFFFFF === self::$rand[$i]; --$i) {
|
||||
self::$rand[$i] = 0;
|
||||
|
@ -31,11 +31,27 @@ class UuidV1 extends Uuid
|
||||
|
||||
public function getDateTime(): \DateTimeImmutable
|
||||
{
|
||||
return BinaryUtil::timeToDateTime('0'.substr($this->uid, 15, 3).substr($this->uid, 9, 4).substr($this->uid, 0, 8));
|
||||
return BinaryUtil::hexToDateTime('0'.substr($this->uid, 15, 3).substr($this->uid, 9, 4).substr($this->uid, 0, 8));
|
||||
}
|
||||
|
||||
public function getNode(): string
|
||||
{
|
||||
return uuid_mac($this->uid);
|
||||
}
|
||||
|
||||
public static function generate(\DateTimeInterface $time = null, Uuid $node = null): string
|
||||
{
|
||||
$uuid = uuid_create(static::TYPE);
|
||||
|
||||
if (null !== $time) {
|
||||
$time = BinaryUtil::dateTimeToHex($time);
|
||||
$uuid = substr($time, 8).'-'.substr($time, 4, 4).'-1'.substr($time, 1, 3).substr($uuid, 18);
|
||||
}
|
||||
|
||||
if ($node) {
|
||||
$uuid = substr($uuid, 0, 24).substr($node->uid, 24);
|
||||
}
|
||||
|
||||
return $uuid;
|
||||
}
|
||||
}
|
||||
|
@ -27,22 +27,7 @@ class UuidV6 extends Uuid
|
||||
public function __construct(string $uuid = null)
|
||||
{
|
||||
if (null === $uuid) {
|
||||
$uuid = uuid_create(\UUID_TYPE_TIME);
|
||||
$this->uid = substr($uuid, 15, 3).substr($uuid, 9, 4).$uuid[0].'-'.substr($uuid, 1, 4).'-6'.substr($uuid, 5, 3).substr($uuid, 18, 6);
|
||||
|
||||
// uuid_create() returns a stable "node" that can leak the MAC of the host, but
|
||||
// UUIDv6 prefers a truly random number here, let's XOR both to preserve the entropy
|
||||
|
||||
if (null === self::$seed) {
|
||||
self::$seed = [random_int(0, 0xffffff), random_int(0, 0xffffff)];
|
||||
}
|
||||
|
||||
$node = unpack('N2', hex2bin('00'.substr($uuid, 24, 6)).hex2bin('00'.substr($uuid, 30)));
|
||||
|
||||
$this->uid .= sprintf('%06x%06x',
|
||||
(self::$seed[0] ^ $node[1]) | 0x010000,
|
||||
self::$seed[1] ^ $node[2]
|
||||
);
|
||||
$this->uid = static::generate();
|
||||
} else {
|
||||
parent::__construct($uuid);
|
||||
}
|
||||
@ -50,11 +35,35 @@ class UuidV6 extends Uuid
|
||||
|
||||
public function getDateTime(): \DateTimeImmutable
|
||||
{
|
||||
return BinaryUtil::timeToDateTime('0'.substr($this->uid, 0, 8).substr($this->uid, 9, 4).substr($this->uid, 15, 3));
|
||||
return BinaryUtil::hexToDateTime('0'.substr($this->uid, 0, 8).substr($this->uid, 9, 4).substr($this->uid, 15, 3));
|
||||
}
|
||||
|
||||
public function getNode(): string
|
||||
{
|
||||
return substr($this->uid, 24);
|
||||
}
|
||||
|
||||
public static function generate(\DateTimeInterface $time = null, Uuid $node = null): string
|
||||
{
|
||||
$uuidV1 = UuidV1::generate($time, $node);
|
||||
$uuid = substr($uuidV1, 15, 3).substr($uuidV1, 9, 4).$uuidV1[0].'-'.substr($uuidV1, 1, 4).'-6'.substr($uuidV1, 5, 3).substr($uuidV1, 18, 6);
|
||||
|
||||
if ($node) {
|
||||
return $uuid.substr($uuidV1, 24);
|
||||
}
|
||||
|
||||
// uuid_create() returns a stable "node" that can leak the MAC of the host, but
|
||||
// UUIDv6 prefers a truly random number here, let's XOR both to preserve the entropy
|
||||
|
||||
if (null === self::$seed) {
|
||||
self::$seed = [random_int(0, 0xffffff), random_int(0, 0xffffff)];
|
||||
}
|
||||
|
||||
$node = unpack('N2', hex2bin('00'.substr($uuidV1, 24, 6)).hex2bin('00'.substr($uuidV1, 30)));
|
||||
|
||||
return $uuid.sprintf('%06x%06x',
|
||||
(self::$seed[0] ^ $node[1]) | 0x010000,
|
||||
self::$seed[1] ^ $node[2]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user