update patch 1
This commit is contained in:
Vendored
+8
-3
@@ -14,7 +14,7 @@ namespace Symfony\Component\Uid;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
abstract class AbstractUid implements \JsonSerializable, \Stringable
|
||||
abstract class AbstractUid implements \JsonSerializable, \Stringable, HashableInterface
|
||||
{
|
||||
/**
|
||||
* The identifier in its canonic representation.
|
||||
@@ -95,7 +95,7 @@ abstract class AbstractUid implements \JsonSerializable, \Stringable
|
||||
*/
|
||||
public function toBase58(): string
|
||||
{
|
||||
return strtr(sprintf('%022s', BinaryUtil::toBase($this->toBinary(), BinaryUtil::BASE58)), '0', '1');
|
||||
return strtr(\sprintf('%022s', BinaryUtil::toBase($this->toBinary(), BinaryUtil::BASE58)), '0', '1');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +108,7 @@ abstract class AbstractUid implements \JsonSerializable, \Stringable
|
||||
public function toBase32(): string
|
||||
{
|
||||
$uid = bin2hex($this->toBinary());
|
||||
$uid = sprintf('%02s%04s%04s%04s%04s%04s%04s',
|
||||
$uid = \sprintf('%02s%04s%04s%04s%04s%04s%04s',
|
||||
base_convert(substr($uid, 0, 2), 16, 32),
|
||||
base_convert(substr($uid, 2, 5), 16, 32),
|
||||
base_convert(substr($uid, 7, 5), 16, 32),
|
||||
@@ -161,6 +161,11 @@ abstract class AbstractUid implements \JsonSerializable, \Stringable
|
||||
return $this->uid === $other->uid;
|
||||
}
|
||||
|
||||
public function hash(): string
|
||||
{
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
public function compare(self $other): int
|
||||
{
|
||||
return (\strlen($this->uid) - \strlen($other->uid)) ?: ($this->uid <=> $other->uid);
|
||||
|
||||
Vendored
+7
@@ -1,6 +1,13 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
7.2
|
||||
---
|
||||
|
||||
* Make `AbstractUid` implement `Ds\Hashable` if available
|
||||
* Add support for binary, base-32 and base-58 representations in `Uuid::isValid()`
|
||||
* Add the `Uuid::FORMAT_RFC_9562` constant to validate UUIDs in the RFC 9562 format
|
||||
|
||||
7.1
|
||||
---
|
||||
|
||||
|
||||
+4
-3
@@ -37,7 +37,7 @@ class GenerateUlidCommand extends Command
|
||||
->setDefinition([
|
||||
new InputOption('time', null, InputOption::VALUE_REQUIRED, 'The ULID timestamp: a parsable date/time string'),
|
||||
new InputOption('count', 'c', InputOption::VALUE_REQUIRED, 'The number of ULID to generate', 1),
|
||||
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, sprintf('The ULID output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'base32'),
|
||||
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, \sprintf('The ULID output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'base32'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command generates a ULID.
|
||||
@@ -68,7 +68,7 @@ EOF
|
||||
try {
|
||||
$time = new \DateTimeImmutable($time);
|
||||
} catch (\Exception $e) {
|
||||
$io->error(sprintf('Invalid timestamp "%s": %s', $time, str_replace('DateTimeImmutable::__construct(): ', '', $e->getMessage())));
|
||||
$io->error(\sprintf('Invalid timestamp "%s": %s', $time, str_replace('DateTimeImmutable::__construct(): ', '', $e->getMessage())));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -79,7 +79,7 @@ EOF
|
||||
if (\in_array($formatOption, $this->getAvailableFormatOptions(), true)) {
|
||||
$format = 'to'.ucfirst($formatOption);
|
||||
} else {
|
||||
$io->error(sprintf('Invalid format "%s", supported formats are "%s".', $formatOption, implode('", "', $this->getAvailableFormatOptions())));
|
||||
$io->error(\sprintf('Invalid format "%s", supported formats are "%s".', $formatOption, implode('", "', $this->getAvailableFormatOptions())));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -105,6 +105,7 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private function getAvailableFormatOptions(): array
|
||||
{
|
||||
return [
|
||||
|
||||
+6
-5
@@ -42,7 +42,7 @@ class GenerateUuidCommand extends Command
|
||||
new InputOption('namespace', null, InputOption::VALUE_REQUIRED, 'The UUID to use at the namespace for named-based UUIDs, predefined namespaces keywords "dns", "url", "oid" and "x500" are accepted'),
|
||||
new InputOption('random-based', null, InputOption::VALUE_NONE, 'To generate a random-based UUID'),
|
||||
new InputOption('count', 'c', InputOption::VALUE_REQUIRED, 'The number of UUID to generate', 1),
|
||||
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, sprintf('The UUID output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'rfc4122'),
|
||||
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, \sprintf('The UUID output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'rfc4122'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> generates a UUID.
|
||||
@@ -115,7 +115,7 @@ EOF
|
||||
try {
|
||||
$node = Uuid::fromString($node);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$io->error(sprintf('Invalid node "%s": %s', $node, $e->getMessage()));
|
||||
$io->error(\sprintf('Invalid node "%s": %s', $node, $e->getMessage()));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -124,7 +124,7 @@ EOF
|
||||
try {
|
||||
new \DateTimeImmutable($time);
|
||||
} catch (\Exception $e) {
|
||||
$io->error(sprintf('Invalid timestamp "%s": %s', $time, str_replace('DateTimeImmutable::__construct(): ', '', $e->getMessage())));
|
||||
$io->error(\sprintf('Invalid timestamp "%s": %s', $time, str_replace('DateTimeImmutable::__construct(): ', '', $e->getMessage())));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -137,7 +137,7 @@ EOF
|
||||
try {
|
||||
$namespace = Uuid::fromString($namespace);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$io->error(sprintf('Invalid namespace "%s": %s', $namespace, $e->getMessage()));
|
||||
$io->error(\sprintf('Invalid namespace "%s": %s', $namespace, $e->getMessage()));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -168,7 +168,7 @@ EOF
|
||||
if (\in_array($formatOption, $this->getAvailableFormatOptions(), true)) {
|
||||
$format = 'to'.ucfirst($formatOption);
|
||||
} else {
|
||||
$io->error(sprintf('Invalid format "%s", supported formats are "%s".', $formatOption, implode('", "', $this->getAvailableFormatOptions())));
|
||||
$io->error(\sprintf('Invalid format "%s", supported formats are "%s".', $formatOption, implode('", "', $this->getAvailableFormatOptions())));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -194,6 +194,7 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private function getAvailableFormatOptions(): array
|
||||
{
|
||||
return [
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ class UuidFactory
|
||||
$namespace ??= $this->nameBasedNamespace;
|
||||
|
||||
if (null === $namespace) {
|
||||
throw new \LogicException(sprintf('A namespace should be defined when using "%s()".', __METHOD__));
|
||||
throw new \LogicException(\sprintf('A namespace should be defined when using "%s()".', __METHOD__));
|
||||
}
|
||||
|
||||
return new NameBasedUuidFactory($this->nameBasedClass, $this->getNamespace($namespace));
|
||||
|
||||
Vendored
+6
-6
@@ -36,7 +36,7 @@ class Ulid extends AbstractUid implements TimeBasedUidInterface
|
||||
$this->uid = $ulid;
|
||||
} else {
|
||||
if (!self::isValid($ulid)) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid ULID: "%s".', $ulid));
|
||||
throw new \InvalidArgumentException(\sprintf('Invalid ULID: "%s".', $ulid));
|
||||
}
|
||||
|
||||
$this->uid = strtoupper($ulid);
|
||||
@@ -73,7 +73,7 @@ class Ulid extends AbstractUid implements TimeBasedUidInterface
|
||||
}
|
||||
|
||||
$ulid = bin2hex($ulid);
|
||||
$ulid = sprintf('%02s%04s%04s%04s%04s%04s%04s',
|
||||
$ulid = \sprintf('%02s%04s%04s%04s%04s%04s%04s',
|
||||
base_convert(substr($ulid, 0, 2), 16, 32),
|
||||
base_convert(substr($ulid, 2, 5), 16, 32),
|
||||
base_convert(substr($ulid, 7, 5), 16, 32),
|
||||
@@ -101,7 +101,7 @@ class Ulid extends AbstractUid implements TimeBasedUidInterface
|
||||
{
|
||||
$ulid = strtr($this->uid, 'ABCDEFGHJKMNPQRSTVWXYZ', 'abcdefghijklmnopqrstuv');
|
||||
|
||||
$ulid = sprintf('%02s%05s%05s%05s%05s%05s%05s',
|
||||
$ulid = \sprintf('%02s%05s%05s%05s%05s%05s%05s',
|
||||
base_convert(substr($ulid, 0, 2), 32, 16),
|
||||
base_convert(substr($ulid, 2, 4), 32, 16),
|
||||
base_convert(substr($ulid, 6, 4), 32, 16),
|
||||
@@ -133,7 +133,7 @@ class Ulid extends AbstractUid implements TimeBasedUidInterface
|
||||
if (\PHP_INT_SIZE >= 8) {
|
||||
$time = (string) hexdec(base_convert($time, 32, 16));
|
||||
} else {
|
||||
$time = sprintf('%02s%05s%05s',
|
||||
$time = \sprintf('%02s%05s%05s',
|
||||
base_convert(substr($time, 0, 2), 32, 16),
|
||||
base_convert(substr($time, 2, 4), 32, 16),
|
||||
base_convert(substr($time, 6, 4), 32, 16)
|
||||
@@ -190,14 +190,14 @@ class Ulid extends AbstractUid implements TimeBasedUidInterface
|
||||
$time = base_convert($time, 10, 32);
|
||||
} else {
|
||||
$time = str_pad(bin2hex(BinaryUtil::fromBase($time, BinaryUtil::BASE10)), 12, '0', \STR_PAD_LEFT);
|
||||
$time = sprintf('%s%04s%04s',
|
||||
$time = \sprintf('%s%04s%04s',
|
||||
base_convert(substr($time, 0, 2), 16, 32),
|
||||
base_convert(substr($time, 2, 5), 16, 32),
|
||||
base_convert(substr($time, 7, 5), 16, 32)
|
||||
);
|
||||
}
|
||||
|
||||
return strtr(sprintf('%010s%04s%04s%04s%04s',
|
||||
return strtr(\sprintf('%010s%04s%04s%04s%04s',
|
||||
$time,
|
||||
base_convert(self::$rand[1], 10, 32),
|
||||
base_convert(self::$rand[2], 10, 32),
|
||||
|
||||
Vendored
+62
-19
@@ -23,6 +23,13 @@ class Uuid extends AbstractUid
|
||||
public const NAMESPACE_OID = '6ba7b812-9dad-11d1-80b4-00c04fd430c8';
|
||||
public const NAMESPACE_X500 = '6ba7b814-9dad-11d1-80b4-00c04fd430c8';
|
||||
|
||||
public const FORMAT_BINARY = 1;
|
||||
public const FORMAT_BASE_32 = 1 << 1;
|
||||
public const FORMAT_BASE_58 = 1 << 2;
|
||||
public const FORMAT_RFC_4122 = 1 << 3;
|
||||
public const FORMAT_RFC_9562 = self::FORMAT_RFC_4122;
|
||||
public const FORMAT_ALL = -1;
|
||||
|
||||
protected const TYPE = 0;
|
||||
protected const NIL = '00000000-0000-0000-0000-000000000000';
|
||||
protected const MAX = 'ffffffff-ffff-ffff-ffff-ffffffffffff';
|
||||
@@ -32,34 +39,19 @@ class Uuid extends AbstractUid
|
||||
$type = preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$}Di', $uuid) ? (int) $uuid[14] : false;
|
||||
|
||||
if (false === $type || (static::TYPE ?: $type) !== $type) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid UUID%s: "%s".', static::TYPE ? 'v'.static::TYPE : '', $uuid));
|
||||
throw new \InvalidArgumentException(\sprintf('Invalid UUID%s: "%s".', static::TYPE ? 'v'.static::TYPE : '', $uuid));
|
||||
}
|
||||
|
||||
$this->uid = strtolower($uuid);
|
||||
|
||||
if ($checkVariant && !\in_array($this->uid[19], ['8', '9', 'a', 'b'], true)) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid UUID%s: "%s".', static::TYPE ? 'v'.static::TYPE : '', $uuid));
|
||||
throw new \InvalidArgumentException(\sprintf('Invalid UUID%s: "%s".', static::TYPE ? 'v'.static::TYPE : '', $uuid));
|
||||
}
|
||||
}
|
||||
|
||||
public static function fromString(string $uuid): static
|
||||
{
|
||||
if (22 === \strlen($uuid) && 22 === strspn($uuid, BinaryUtil::BASE58[''])) {
|
||||
$uuid = str_pad(BinaryUtil::fromBase($uuid, BinaryUtil::BASE58), 16, "\0", \STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
if (16 === \strlen($uuid)) {
|
||||
// don't use uuid_unparse(), it's slower
|
||||
$uuid = bin2hex($uuid);
|
||||
$uuid = substr_replace($uuid, '-', 8, 0);
|
||||
$uuid = substr_replace($uuid, '-', 13, 0);
|
||||
$uuid = substr_replace($uuid, '-', 18, 0);
|
||||
$uuid = substr_replace($uuid, '-', 23, 0);
|
||||
} elseif (26 === \strlen($uuid) && Ulid::isValid($uuid)) {
|
||||
$ulid = new NilUlid();
|
||||
$ulid->uid = strtoupper($uuid);
|
||||
$uuid = $ulid->toRfc4122();
|
||||
}
|
||||
$uuid = self::transformToRfc9562($uuid, self::FORMAT_ALL);
|
||||
|
||||
if (__CLASS__ !== static::class || 36 !== \strlen($uuid)) {
|
||||
return new static($uuid);
|
||||
@@ -130,8 +122,21 @@ class Uuid extends AbstractUid
|
||||
return new UuidV8($uuid);
|
||||
}
|
||||
|
||||
public static function isValid(string $uuid): bool
|
||||
/**
|
||||
* @param int-mask-of<Uuid::FORMAT_*> $format
|
||||
*/
|
||||
public static function isValid(string $uuid /* , int $format = self::FORMAT_RFC_9562 */): bool
|
||||
{
|
||||
$format = 1 < \func_num_args() ? func_get_arg(1) : self::FORMAT_RFC_9562;
|
||||
|
||||
if (36 === \strlen($uuid) && !($format & self::FORMAT_RFC_9562)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === $uuid = self::transformToRfc9562($uuid, $format)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self::NIL === $uuid && \in_array(static::class, [__CLASS__, NilUuid::class], true)) {
|
||||
return true;
|
||||
}
|
||||
@@ -182,4 +187,42 @@ class Uuid extends AbstractUid
|
||||
|
||||
return substr_replace($uuid, '-', 23, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a binary string, a base-32 string or a base-58 string to a RFC9562 string.
|
||||
*
|
||||
* @param int-mask-of<Uuid::FORMAT_*> $format
|
||||
*
|
||||
* @return string|false The RFC9562 string or false if the format doesn't match the input
|
||||
*/
|
||||
private static function transformToRfc9562(string $uuid, int $format): string|false
|
||||
{
|
||||
$inputUuid = $uuid;
|
||||
$fromBase58 = false;
|
||||
if (22 === \strlen($uuid) && 22 === strspn($uuid, BinaryUtil::BASE58['']) && $format & self::FORMAT_BASE_58) {
|
||||
$uuid = str_pad(BinaryUtil::fromBase($uuid, BinaryUtil::BASE58), 16, "\0", \STR_PAD_LEFT);
|
||||
$fromBase58 = true;
|
||||
}
|
||||
|
||||
// base-58 are always transformed to binary string, but they must only be valid when the format is FORMAT_BASE_58
|
||||
if (16 === \strlen($uuid) && $format & self::FORMAT_BINARY || $fromBase58 && $format & self::FORMAT_BASE_58) {
|
||||
// don't use uuid_unparse(), it's slower
|
||||
$uuid = bin2hex($uuid);
|
||||
$uuid = substr_replace($uuid, '-', 8, 0);
|
||||
$uuid = substr_replace($uuid, '-', 13, 0);
|
||||
$uuid = substr_replace($uuid, '-', 18, 0);
|
||||
$uuid = substr_replace($uuid, '-', 23, 0);
|
||||
} elseif (26 === \strlen($uuid) && Ulid::isValid($uuid) && $format & self::FORMAT_BASE_32) {
|
||||
$ulid = new NilUlid();
|
||||
$ulid->uid = strtoupper($uuid);
|
||||
$uuid = $ulid->toRfc4122();
|
||||
}
|
||||
|
||||
if ($inputUuid === $uuid && !($format & self::FORMAT_RFC_9562)) {
|
||||
// input format doesn't match the input string
|
||||
return false;
|
||||
}
|
||||
|
||||
return $uuid;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -66,7 +66,7 @@ class UuidV1 extends Uuid implements TimeBasedUidInterface
|
||||
$seq = substr($uuid, 19, 4);
|
||||
|
||||
do {
|
||||
self::$clockSeq = sprintf('%04x', random_int(0, 0x3FFF) | 0x8000);
|
||||
self::$clockSeq = \sprintf('%04x', random_int(0, 0x3FFF) | 0x8000);
|
||||
} while ($seq === self::$clockSeq);
|
||||
|
||||
$seq = self::$clockSeq;
|
||||
|
||||
Vendored
+2
-2
@@ -59,7 +59,7 @@ class UuidV6 extends Uuid implements TimeBasedUidInterface
|
||||
$time = substr($time, 1);
|
||||
}
|
||||
|
||||
return new UuidV7(substr_replace(sprintf(
|
||||
return new UuidV7(substr_replace(\sprintf(
|
||||
'%012s-7%s-%s%s-%s%06s',
|
||||
\PHP_INT_SIZE >= 8 ? dechex($ms) : bin2hex(BinaryUtil::fromBase($ms, BinaryUtil::BASE10)),
|
||||
substr($uuid, -6, 3),
|
||||
@@ -85,7 +85,7 @@ class UuidV6 extends Uuid implements TimeBasedUidInterface
|
||||
if (!isset(self::$node)) {
|
||||
$seed = [random_int(0, 0xFFFFFF), random_int(0, 0xFFFFFF)];
|
||||
$node = unpack('N2', hex2bin('00'.substr($uuidV1, 24, 6)).hex2bin('00'.substr($uuidV1, 30)));
|
||||
self::$node = sprintf('%06x%06x', ($seed[0] ^ $node[1]) | 0x010000, $seed[1] ^ $node[2]);
|
||||
self::$node = \sprintf('%06x%06x', ($seed[0] ^ $node[1]) | 0x010000, $seed[1] ^ $node[2]);
|
||||
}
|
||||
|
||||
return $uuid.self::$node;
|
||||
|
||||
Vendored
+1
-1
@@ -113,7 +113,7 @@ class UuidV7 extends Uuid implements TimeBasedUidInterface
|
||||
$time = bin2hex(BinaryUtil::fromBase($time, BinaryUtil::BASE10));
|
||||
}
|
||||
|
||||
return substr_replace(sprintf('%012s-%04x-%04x-%04x%04x%04x',
|
||||
return substr_replace(\sprintf('%012s-%04x-%04x-%04x%04x%04x',
|
||||
$time,
|
||||
0x7000 | (self::$rand[1] << 2) | (self::$rand[2] >> 14),
|
||||
0x8000 | (self::$rand[2] & 0x3FFF),
|
||||
|
||||
Reference in New Issue
Block a user