resolved conflicts

This commit is contained in:
2025-03-31 03:46:05 +03:00
6454 changed files with 1520539 additions and 9 deletions
+178
View File
@@ -0,0 +1,178 @@
<?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;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
abstract class AbstractUid implements \JsonSerializable, \Stringable
{
/**
* The identifier in its canonic representation.
*/
protected $uid;
/**
* Whether the passed value is valid for the constructor of the current class.
*/
abstract public static function isValid(string $uid): bool;
/**
* Creates an AbstractUid from an identifier represented in any of the supported formats.
*
* @throws \InvalidArgumentException When the passed value is not valid
*/
abstract public static function fromString(string $uid): static;
/**
* @throws \InvalidArgumentException When the passed value is not valid
*/
public static function fromBinary(string $uid): static
{
if (16 !== \strlen($uid)) {
throw new \InvalidArgumentException('Invalid binary uid provided.');
}
return static::fromString($uid);
}
/**
* @throws \InvalidArgumentException When the passed value is not valid
*/
public static function fromBase58(string $uid): static
{
if (22 !== \strlen($uid)) {
throw new \InvalidArgumentException('Invalid base-58 uid provided.');
}
return static::fromString($uid);
}
/**
* @throws \InvalidArgumentException When the passed value is not valid
*/
public static function fromBase32(string $uid): static
{
if (26 !== \strlen($uid)) {
throw new \InvalidArgumentException('Invalid base-32 uid provided.');
}
return static::fromString($uid);
}
/**
* @param string $uid A valid RFC 9562/4122 uid
*
* @throws \InvalidArgumentException When the passed value is not valid
*/
public static function fromRfc4122(string $uid): static
{
if (36 !== \strlen($uid)) {
throw new \InvalidArgumentException('Invalid RFC4122 uid provided.');
}
return static::fromString($uid);
}
/**
* Returns the identifier as a raw binary string.
*/
abstract public function toBinary(): string;
/**
* Returns the identifier as a base58 case sensitive string.
*
* @example 2AifFTC3zXgZzK5fPrrprL (len=22)
*/
public function toBase58(): string
{
return strtr(sprintf('%022s', BinaryUtil::toBase($this->toBinary(), BinaryUtil::BASE58)), '0', '1');
}
/**
* Returns the identifier as a base32 case insensitive string.
*
* @see https://tools.ietf.org/html/rfc4648#section-6
*
* @example 09EJ0S614A9FXVG9C5537Q9ZE1 (len=26)
*/
public function toBase32(): string
{
$uid = bin2hex($this->toBinary());
$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),
base_convert(substr($uid, 12, 5), 16, 32),
base_convert(substr($uid, 17, 5), 16, 32),
base_convert(substr($uid, 22, 5), 16, 32),
base_convert(substr($uid, 27, 5), 16, 32)
);
return strtr($uid, 'abcdefghijklmnopqrstuv', 'ABCDEFGHJKMNPQRSTVWXYZ');
}
/**
* Returns the identifier as a RFC 9562/4122 case insensitive string.
*
* @see https://datatracker.ietf.org/doc/html/rfc9562/#section-4
*
* @example 09748193-048a-4bfb-b825-8528cf74fdc1 (len=36)
*/
public function toRfc4122(): string
{
// don't use uuid_unparse(), it's slower
$uuid = bin2hex($this->toBinary());
$uuid = substr_replace($uuid, '-', 8, 0);
$uuid = substr_replace($uuid, '-', 13, 0);
$uuid = substr_replace($uuid, '-', 18, 0);
return substr_replace($uuid, '-', 23, 0);
}
/**
* Returns the identifier as a prefixed hexadecimal case insensitive string.
*
* @example 0x09748193048a4bfbb8258528cf74fdc1 (len=34)
*/
public function toHex(): string
{
return '0x'.bin2hex($this->toBinary());
}
/**
* Returns whether the argument is an AbstractUid and contains the same value as the current instance.
*/
public function equals(mixed $other): bool
{
if (!$other instanceof self) {
return false;
}
return $this->uid === $other->uid;
}
public function compare(self $other): int
{
return (\strlen($this->uid) - \strlen($other->uid)) ?: ($this->uid <=> $other->uid);
}
public function __toString(): string
{
return $this->uid;
}
public function jsonSerialize(): string
{
return $this->uid;
}
}
+175
View File
@@ -0,0 +1,175 @@
<?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;
/**
* @internal
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class BinaryUtil
{
public const BASE10 = [
'' => '0123456789',
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
];
public const BASE58 = [
'' => '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
1 => 0, 1, 2, 3, 4, 5, 6, 7, 8, 'A' => 9,
'B' => 10, 'C' => 11, 'D' => 12, 'E' => 13, 'F' => 14, 'G' => 15,
'H' => 16, 'J' => 17, 'K' => 18, 'L' => 19, 'M' => 20, 'N' => 21,
'P' => 22, 'Q' => 23, 'R' => 24, 'S' => 25, 'T' => 26, 'U' => 27,
'V' => 28, 'W' => 29, 'X' => 30, 'Y' => 31, 'Z' => 32, 'a' => 33,
'b' => 34, 'c' => 35, 'd' => 36, 'e' => 37, 'f' => 38, 'g' => 39,
'h' => 40, 'i' => 41, 'j' => 42, 'k' => 43, 'm' => 44, 'n' => 45,
'o' => 46, 'p' => 47, 'q' => 48, 'r' => 49, 's' => 50, 't' => 51,
'u' => 52, 'v' => 53, 'w' => 54, 'x' => 55, 'y' => 56, 'z' => 57,
];
// https://datatracker.ietf.org/doc/html/rfc9562#section-5.1
// 0x01b21dd213814000 is the number of 100-ns intervals between the
// UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
private const TIME_OFFSET_INT = 0x01B21DD213814000;
private const TIME_OFFSET_BIN = "\x01\xb2\x1d\xd2\x13\x81\x40\x00";
private const TIME_OFFSET_COM1 = "\xfe\x4d\xe2\x2d\xec\x7e\xbf\xff";
private const TIME_OFFSET_COM2 = "\xfe\x4d\xe2\x2d\xec\x7e\xc0\x00";
public static function toBase(string $bytes, array $map): string
{
$base = \strlen($alphabet = $map['']);
$bytes = array_values(unpack(\PHP_INT_SIZE >= 8 ? 'n*' : 'C*', $bytes));
$digits = '';
while ($count = \count($bytes)) {
$quotient = [];
$remainder = 0;
for ($i = 0; $i !== $count; ++$i) {
$carry = $bytes[$i] + ($remainder << (\PHP_INT_SIZE >= 8 ? 16 : 8));
$digit = intdiv($carry, $base);
$remainder = $carry % $base;
if ($digit || $quotient) {
$quotient[] = $digit;
}
}
$digits = $alphabet[$remainder].$digits;
$bytes = $quotient;
}
return $digits;
}
public static function fromBase(string $digits, array $map): string
{
$base = \strlen($map['']);
$count = \strlen($digits);
$bytes = [];
while ($count) {
$quotient = [];
$remainder = 0;
for ($i = 0; $i !== $count; ++$i) {
$carry = ($bytes ? $digits[$i] : $map[$digits[$i]]) + $remainder * $base;
if (\PHP_INT_SIZE >= 8) {
$digit = $carry >> 16;
$remainder = $carry & 0xFFFF;
} else {
$digit = $carry >> 8;
$remainder = $carry & 0xFF;
}
if ($digit || $quotient) {
$quotient[] = $digit;
}
}
$bytes[] = $remainder;
$count = \count($digits = $quotient);
}
return pack(\PHP_INT_SIZE >= 8 ? 'n*' : 'C*', ...array_reverse($bytes));
}
public static function add(string $a, string $b): string
{
$carry = 0;
for ($i = 7; 0 <= $i; --$i) {
$carry += \ord($a[$i]) + \ord($b[$i]);
$a[$i] = \chr($carry & 0xFF);
$carry >>= 8;
}
return $a;
}
/**
* @param string $time Count of 100-nanosecond intervals since the UUID epoch 1582-10-15 00:00:00 in hexadecimal
*/
public static function hexToDateTime(string $time): \DateTimeImmutable
{
if (\PHP_INT_SIZE >= 8) {
$time = (string) (hexdec($time) - self::TIME_OFFSET_INT);
} else {
$time = str_pad(hex2bin($time), 8, "\0", \STR_PAD_LEFT);
if (self::TIME_OFFSET_BIN <= $time) {
$time = self::add($time, self::TIME_OFFSET_COM2);
$time[0] = $time[0] & "\x7F";
$time = self::toBase($time, self::BASE10);
} else {
$time = self::add($time, self::TIME_OFFSET_COM1);
$time = '-'.self::toBase($time ^ "\xff\xff\xff\xff\xff\xff\xff\xff", self::BASE10);
}
}
if (9 > \strlen($time)) {
$time = '-' === $time[0] ? '-'.str_pad(substr($time, 1), 8, '0', \STR_PAD_LEFT) : str_pad($time, 8, '0', \STR_PAD_LEFT);
}
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);
}
}
+185
View File
@@ -0,0 +1,185 @@
<?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;
/**
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*
* @see https://datatracker.ietf.org/doc/html/rfc9562/#section-6.6 for details about namespaces
*/
class Uuid extends AbstractUid
{
public const NAMESPACE_DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
public const NAMESPACE_URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
public const NAMESPACE_OID = '6ba7b812-9dad-11d1-80b4-00c04fd430c8';
public const NAMESPACE_X500 = '6ba7b814-9dad-11d1-80b4-00c04fd430c8';
protected const TYPE = 0;
protected const NIL = '00000000-0000-0000-0000-000000000000';
protected const MAX = 'ffffffff-ffff-ffff-ffff-ffffffffffff';
public function __construct(string $uuid, bool $checkVariant = false)
{
$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));
}
$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));
}
}
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();
}
if (__CLASS__ !== static::class || 36 !== \strlen($uuid)) {
return new static($uuid);
}
if (self::NIL === $uuid) {
return new NilUuid();
}
if (self::MAX === $uuid = strtr($uuid, 'F', 'f')) {
return new MaxUuid();
}
if (!\in_array($uuid[19], ['8', '9', 'a', 'b', 'A', 'B'], true)) {
return new self($uuid);
}
return match ((int) $uuid[14]) {
UuidV1::TYPE => new UuidV1($uuid),
UuidV3::TYPE => new UuidV3($uuid),
UuidV4::TYPE => new UuidV4($uuid),
UuidV5::TYPE => new UuidV5($uuid),
UuidV6::TYPE => new UuidV6($uuid),
UuidV7::TYPE => new UuidV7($uuid),
UuidV8::TYPE => new UuidV8($uuid),
default => new self($uuid),
};
}
final public static function v1(): UuidV1
{
return new UuidV1();
}
final public static function v3(self $namespace, string $name): UuidV3
{
// don't use uuid_generate_md5(), some versions are buggy
$uuid = md5(hex2bin(str_replace('-', '', $namespace->uid)).$name, true);
return new UuidV3(self::format($uuid, '-3'));
}
final public static function v4(): UuidV4
{
return new UuidV4();
}
final public static function v5(self $namespace, string $name): UuidV5
{
// don't use uuid_generate_sha1(), some versions are buggy
$uuid = substr(sha1(hex2bin(str_replace('-', '', $namespace->uid)).$name, true), 0, 16);
return new UuidV5(self::format($uuid, '-5'));
}
final public static function v6(): UuidV6
{
return new UuidV6();
}
final public static function v7(): UuidV7
{
return new UuidV7();
}
final public static function v8(string $uuid): UuidV8
{
return new UuidV8($uuid);
}
public static function isValid(string $uuid): bool
{
if (self::NIL === $uuid && \in_array(static::class, [__CLASS__, NilUuid::class], true)) {
return true;
}
if (self::MAX === strtr($uuid, 'F', 'f') && \in_array(static::class, [__CLASS__, MaxUuid::class], true)) {
return true;
}
if (!preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){2}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$}Di', $uuid)) {
return false;
}
return __CLASS__ === static::class || static::TYPE === (int) $uuid[14];
}
public function toBinary(): string
{
return uuid_parse($this->uid);
}
/**
* Returns the identifier as a RFC 9562/4122 case insensitive string.
*
* @see https://datatracker.ietf.org/doc/html/rfc9562/#section-4
*
* @example 09748193-048a-4bfb-b825-8528cf74fdc1 (len=36)
*/
public function toRfc4122(): string
{
return $this->uid;
}
public function compare(AbstractUid $other): int
{
if (false !== $cmp = uuid_compare($this->uid, $other->uid)) {
return $cmp;
}
return parent::compare($other);
}
private static function format(string $uuid, string $version): string
{
$uuid[8] = $uuid[8] & "\x3F" | "\x80";
$uuid = substr_replace(bin2hex($uuid), '-', 8, 0);
$uuid = substr_replace($uuid, $version, 13, 1);
$uuid = substr_replace($uuid, '-', 18, 0);
return substr_replace($uuid, '-', 23, 0);
}
}
+73
View File
@@ -0,0 +1,73 @@
<?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;
/**
* A v1 UUID contains a 60-bit timestamp and 62 extra unique bits.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
class UuidV1 extends Uuid implements TimeBasedUidInterface
{
protected const TYPE = 1;
private static string $clockSeq;
public function __construct(?string $uuid = null)
{
if (null === $uuid) {
$this->uid = strtolower(uuid_create(static::TYPE));
} else {
parent::__construct($uuid, true);
}
}
public function getDateTime(): \DateTimeImmutable
{
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 = !$time || !$node ? uuid_create(static::TYPE) : parent::NIL;
if ($time) {
if ($node) {
// use clock_seq from the node
$seq = substr($node->uid, 19, 4);
} elseif (!$seq = self::$clockSeq ?? '') {
// generate a static random clock_seq to prevent any collisions with the real one
$seq = substr($uuid, 19, 4);
do {
self::$clockSeq = sprintf('%04x', random_int(0, 0x3FFF) | 0x8000);
} while ($seq === self::$clockSeq);
$seq = self::$clockSeq;
}
$time = BinaryUtil::dateTimeToHex($time);
$uuid = substr($time, 8).'-'.substr($time, 4, 4).'-1'.substr($time, 1, 3).'-'.$seq.substr($uuid, 23);
}
if ($node) {
$uuid = substr($uuid, 0, 24).substr($node->uid, 24);
}
return $uuid;
}
}