media-cloud-sync
/
includes
/
sdk
/
google
/
ramsey
/
uuid
/
src
/
Generator
/
UnixTimeGenerator.php
UnixTimeGenerator.php in Media Cloud Sync 1.4.1, at includes/sdk/google/ramsey/uuid/src/Generator/UnixTimeGenerator.php
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * This file is part of the ramsey/uuid library |
| 5 | * |
| 6 | * For the full copyright and license information, please view the LICENSE |
| 7 | * file that was distributed with this source code. |
| 8 | * |
| 9 | * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> |
| 10 | * @license http://opensource.org/licenses/MIT MIT |
| 11 | */ |
| 12 | declare (strict_types=1); |
| 13 | namespace Dudlewebs\WPMCS\GCP\Ramsey\Uuid\Generator; |
| 14 | |
| 15 | use Dudlewebs\WPMCS\GCP\Brick\Math\BigInteger; |
| 16 | use DateTimeInterface; |
| 17 | use Dudlewebs\WPMCS\GCP\Ramsey\Uuid\Type\Hexadecimal; |
| 18 | use function assert; |
| 19 | use function hash; |
| 20 | use function pack; |
| 21 | use function str_pad; |
| 22 | use function strlen; |
| 23 | use function substr; |
| 24 | use function substr_replace; |
| 25 | use function unpack; |
| 26 | use const PHP_INT_SIZE; |
| 27 | use const STR_PAD_LEFT; |
| 28 | /** |
| 29 | * UnixTimeGenerator generates bytes, combining a 48-bit timestamp in milliseconds since the Unix Epoch with 80 random bits |
| 30 | * |
| 31 | * Code and concepts within this class are borrowed from the symfony/uid package and are used under the terms of the MIT |
| 32 | * license distributed with symfony/uid. |
| 33 | * |
| 34 | * symfony/uid is copyright (c) Fabien Potencier. |
| 35 | * |
| 36 | * @link https://symfony.com/components/Uid Symfony Uid component |
| 37 | * @link https://github.com/symfony/uid/blob/4f9f537e57261519808a7ce1d941490736522bbc/UuidV7.php Symfony UuidV7 class |
| 38 | * @link https://github.com/symfony/uid/blob/6.2/LICENSE MIT License |
| 39 | */ |
| 40 | class UnixTimeGenerator implements TimeGeneratorInterface |
| 41 | { |
| 42 | private static string $time = ''; |
| 43 | private static ?string $seed = null; |
| 44 | private static int $seedIndex = 0; |
| 45 | /** @var int[] */ |
| 46 | private static array $rand = []; |
| 47 | /** @var int[] */ |
| 48 | private static array $seedParts; |
| 49 | public function __construct(private RandomGeneratorInterface $randomGenerator, private int $intSize = PHP_INT_SIZE) |
| 50 | { |
| 51 | } |
| 52 | /** |
| 53 | * @param Hexadecimal | int | string | null $node Unused in this generator |
| 54 | * @param int | null $clockSeq Unused in this generator |
| 55 | * @param DateTimeInterface | null $dateTime A date-time instance to use when generating bytes |
| 56 | */ |
| 57 | public function generate($node = null, ?int $clockSeq = null, ?DateTimeInterface $dateTime = null) : string |
| 58 | { |
| 59 | if ($dateTime === null) { |
| 60 | $time = \microtime(\false); |
| 61 | $time = substr($time, 11) . substr($time, 2, 3); |
| 62 | } else { |
| 63 | $time = $dateTime->format('Uv'); |
| 64 | } |
| 65 | if ($time > self::$time || $dateTime !== null && $time !== self::$time) { |
| 66 | $this->randomize($time); |
| 67 | } else { |
| 68 | $time = $this->increment(); |
| 69 | } |
| 70 | if ($this->intSize >= 8) { |
| 71 | $time = substr(pack('J', (int) $time), -6); |
| 72 | } else { |
| 73 | $time = str_pad(BigInteger::of($time)->toBytes(\false), 6, "\x00", STR_PAD_LEFT); |
| 74 | } |
| 75 | assert(strlen($time) === 6); |
| 76 | return $time . pack('n*', self::$rand[1], self::$rand[2], self::$rand[3], self::$rand[4], self::$rand[5]); |
| 77 | } |
| 78 | private function randomize(string $time) : void |
| 79 | { |
| 80 | if (self::$seed === null) { |
| 81 | $seed = $this->randomGenerator->generate(16); |
| 82 | self::$seed = $seed; |
| 83 | } else { |
| 84 | $seed = $this->randomGenerator->generate(10); |
| 85 | } |
| 86 | /** @var int[] $rand */ |
| 87 | $rand = unpack('n*', $seed); |
| 88 | $rand[1] &= 0x3ff; |
| 89 | self::$rand = $rand; |
| 90 | self::$time = $time; |
| 91 | } |
| 92 | /** |
| 93 | * Special thanks to Nicolas Grekas (<https://github.com/nicolas-grekas>) for sharing the following information: |
| 94 | * |
| 95 | * Within the same ms, we increment the rand part by a random 24-bit number. |
| 96 | * |
| 97 | * Instead of getting this number from random_bytes(), which is slow, we get it by sha512-hashing self::$seed. This |
| 98 | * produces 64 bytes of entropy, which we need to split in a list of 24-bit numbers. `unpack()` first splits them |
| 99 | * into 16 x 32-bit numbers; we take the first byte of each number to get 5 extra 24-bit numbers. Then, we consume |
| 100 | * each number one-by-one and run this logic every 21 iterations. |
| 101 | * |
| 102 | * `self::$rand` holds the random part of the UUID, split into 5 x 16-bit numbers for x86 portability. We increment |
| 103 | * this random part by the next 24-bit number in the `self::$seedParts` list and decrement `self::$seedIndex`. |
| 104 | */ |
| 105 | private function increment() : string |
| 106 | { |
| 107 | if (self::$seedIndex === 0 && self::$seed !== null) { |
| 108 | self::$seed = hash('sha512', self::$seed, \true); |
| 109 | /** @var int[] $s */ |
| 110 | $s = unpack('l*', self::$seed); |
| 111 | $s[] = $s[1] >> 8 & 0xff0000 | $s[2] >> 16 & 0xff00 | $s[3] >> 24 & 0xff; |
| 112 | $s[] = $s[4] >> 8 & 0xff0000 | $s[5] >> 16 & 0xff00 | $s[6] >> 24 & 0xff; |
| 113 | $s[] = $s[7] >> 8 & 0xff0000 | $s[8] >> 16 & 0xff00 | $s[9] >> 24 & 0xff; |
| 114 | $s[] = $s[10] >> 8 & 0xff0000 | $s[11] >> 16 & 0xff00 | $s[12] >> 24 & 0xff; |
| 115 | $s[] = $s[13] >> 8 & 0xff0000 | $s[14] >> 16 & 0xff00 | $s[15] >> 24 & 0xff; |
| 116 | self::$seedParts = $s; |
| 117 | self::$seedIndex = 21; |
| 118 | } |
| 119 | self::$rand[5] = 0xffff & ($carry = self::$rand[5] + 1 + (self::$seedParts[self::$seedIndex--] & 0xffffff)); |
| 120 | self::$rand[4] = 0xffff & ($carry = self::$rand[4] + ($carry >> 16)); |
| 121 | self::$rand[3] = 0xffff & ($carry = self::$rand[3] + ($carry >> 16)); |
| 122 | self::$rand[2] = 0xffff & ($carry = self::$rand[2] + ($carry >> 16)); |
| 123 | self::$rand[1] += $carry >> 16; |
| 124 | if (0xfc00 & self::$rand[1]) { |
| 125 | $time = self::$time; |
| 126 | $mtime = (int) substr($time, -9); |
| 127 | if ($this->intSize >= 8 || strlen($time) < 10) { |
| 128 | $time = (string) ((int) $time + 1); |
| 129 | } elseif ($mtime === 999999999) { |
| 130 | $time = 1 + (int) substr($time, 0, -9) . '000000000'; |
| 131 | } else { |
| 132 | $mtime++; |
| 133 | $time = substr_replace($time, str_pad((string) $mtime, 9, '0', STR_PAD_LEFT), -9); |
| 134 | } |
| 135 | $this->randomize($time); |
| 136 | } |
| 137 | return self::$time; |
| 138 | } |
| 139 | } |
| 140 |