DefaultTimeGenerator.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. <?php
  2. /**
  3. * This file is part of the ramsey/uuid library
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. *
  8. * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
  9. * @license http://opensource.org/licenses/MIT MIT
  10. */
  11. declare(strict_types=1);
  12. namespace Ramsey\Uuid\Generator;
  13. use Ramsey\Uuid\Converter\TimeConverterInterface;
  14. use Ramsey\Uuid\Exception\InvalidArgumentException;
  15. use Ramsey\Uuid\Exception\RandomSourceException;
  16. use Ramsey\Uuid\Exception\TimeSourceException;
  17. use Ramsey\Uuid\Provider\NodeProviderInterface;
  18. use Ramsey\Uuid\Provider\TimeProviderInterface;
  19. use Ramsey\Uuid\Type\Hexadecimal;
  20. use Throwable;
  21. use function dechex;
  22. use function hex2bin;
  23. use function is_int;
  24. use function pack;
  25. use function preg_match;
  26. use function sprintf;
  27. use function str_pad;
  28. use function strlen;
  29. use const STR_PAD_LEFT;
  30. /**
  31. * DefaultTimeGenerator generates strings of binary data based on a node ID, clock sequence, and the current time
  32. */
  33. class DefaultTimeGenerator implements TimeGeneratorInterface
  34. {
  35. public function __construct(
  36. private NodeProviderInterface $nodeProvider,
  37. private TimeConverterInterface $timeConverter,
  38. private TimeProviderInterface $timeProvider,
  39. ) {
  40. }
  41. /**
  42. * @throws InvalidArgumentException if the parameters contain invalid values
  43. * @throws RandomSourceException if random_int() throws an exception/error
  44. *
  45. * @inheritDoc
  46. */
  47. public function generate($node = null, ?int $clockSeq = null): string
  48. {
  49. if ($node instanceof Hexadecimal) {
  50. $node = $node->toString();
  51. }
  52. $node = $this->getValidNode($node);
  53. if ($clockSeq === null) {
  54. try {
  55. // This does not use "stable storage"; see RFC 9562, section 6.3.
  56. $clockSeq = random_int(0, 0x3fff);
  57. } catch (Throwable $exception) {
  58. throw new RandomSourceException($exception->getMessage(), (int) $exception->getCode(), $exception);
  59. }
  60. }
  61. $time = $this->timeProvider->getTime();
  62. $uuidTime = $this->timeConverter->calculateTime(
  63. $time->getSeconds()->toString(),
  64. $time->getMicroseconds()->toString()
  65. );
  66. $timeHex = str_pad($uuidTime->toString(), 16, '0', STR_PAD_LEFT);
  67. if (strlen($timeHex) !== 16) {
  68. throw new TimeSourceException(sprintf('The generated time of \'%s\' is larger than expected', $timeHex));
  69. }
  70. $timeBytes = (string) hex2bin($timeHex);
  71. return $timeBytes[4] . $timeBytes[5] . $timeBytes[6] . $timeBytes[7]
  72. . $timeBytes[2] . $timeBytes[3] . $timeBytes[0] . $timeBytes[1]
  73. . pack('n*', $clockSeq) . $node;
  74. }
  75. /**
  76. * Uses the node provider given when constructing this instance to get the node ID (usually a MAC address)
  77. *
  78. * @param int | string | null $node A node value that may be used to override the node provider
  79. *
  80. * @return string 6-byte binary string representation of the node
  81. *
  82. * @throws InvalidArgumentException
  83. */
  84. private function getValidNode(int | string | null $node): string
  85. {
  86. if ($node === null) {
  87. $node = $this->nodeProvider->getNode();
  88. }
  89. // Convert the node to hex if it is still an integer.
  90. if (is_int($node)) {
  91. $node = dechex($node);
  92. }
  93. if (!preg_match('/^[A-Fa-f0-9]+$/', (string) $node) || strlen((string) $node) > 12) {
  94. throw new InvalidArgumentException('Invalid node value');
  95. }
  96. return (string) hex2bin(str_pad((string) $node, 12, '0', STR_PAD_LEFT));
  97. }
  98. }