| 148 |
lars |
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 |
|
|
|
13 |
declare(strict_types=1);
|
|
|
14 |
|
|
|
15 |
namespace Ramsey\Uuid\Generator;
|
|
|
16 |
|
|
|
17 |
use Ramsey\Uuid\Converter\TimeConverterInterface;
|
|
|
18 |
use Ramsey\Uuid\Exception\InvalidArgumentException;
|
|
|
19 |
use Ramsey\Uuid\Exception\RandomSourceException;
|
|
|
20 |
use Ramsey\Uuid\Exception\TimeSourceException;
|
|
|
21 |
use Ramsey\Uuid\Provider\NodeProviderInterface;
|
|
|
22 |
use Ramsey\Uuid\Provider\TimeProviderInterface;
|
|
|
23 |
use Ramsey\Uuid\Type\Hexadecimal;
|
|
|
24 |
use Throwable;
|
|
|
25 |
|
|
|
26 |
use function dechex;
|
|
|
27 |
use function hex2bin;
|
|
|
28 |
use function is_int;
|
|
|
29 |
use function pack;
|
|
|
30 |
use function preg_match;
|
|
|
31 |
use function sprintf;
|
|
|
32 |
use function str_pad;
|
|
|
33 |
use function strlen;
|
|
|
34 |
|
|
|
35 |
use const STR_PAD_LEFT;
|
|
|
36 |
|
|
|
37 |
/**
|
|
|
38 |
* DefaultTimeGenerator generates strings of binary data based on a node ID,
|
|
|
39 |
* clock sequence, and the current time
|
|
|
40 |
*/
|
|
|
41 |
class DefaultTimeGenerator implements TimeGeneratorInterface
|
|
|
42 |
{
|
|
|
43 |
public function __construct(
|
|
|
44 |
private NodeProviderInterface $nodeProvider,
|
|
|
45 |
private TimeConverterInterface $timeConverter,
|
|
|
46 |
private TimeProviderInterface $timeProvider
|
|
|
47 |
) {
|
|
|
48 |
}
|
|
|
49 |
|
|
|
50 |
/**
|
|
|
51 |
* @throws InvalidArgumentException if the parameters contain invalid values
|
|
|
52 |
* @throws RandomSourceException if random_int() throws an exception/error
|
|
|
53 |
*
|
|
|
54 |
* @inheritDoc
|
|
|
55 |
*/
|
|
|
56 |
public function generate($node = null, ?int $clockSeq = null): string
|
|
|
57 |
{
|
|
|
58 |
if ($node instanceof Hexadecimal) {
|
|
|
59 |
$node = $node->toString();
|
|
|
60 |
}
|
|
|
61 |
|
|
|
62 |
$node = $this->getValidNode($node);
|
|
|
63 |
|
|
|
64 |
if ($clockSeq === null) {
|
|
|
65 |
try {
|
|
|
66 |
// This does not use "stable storage"; see RFC 4122, Section 4.2.1.1.
|
|
|
67 |
$clockSeq = random_int(0, 0x3fff);
|
|
|
68 |
} catch (Throwable $exception) {
|
|
|
69 |
throw new RandomSourceException(
|
|
|
70 |
$exception->getMessage(),
|
|
|
71 |
(int) $exception->getCode(),
|
|
|
72 |
$exception
|
|
|
73 |
);
|
|
|
74 |
}
|
|
|
75 |
}
|
|
|
76 |
|
|
|
77 |
$time = $this->timeProvider->getTime();
|
|
|
78 |
|
|
|
79 |
$uuidTime = $this->timeConverter->calculateTime(
|
|
|
80 |
$time->getSeconds()->toString(),
|
|
|
81 |
$time->getMicroseconds()->toString()
|
|
|
82 |
);
|
|
|
83 |
|
|
|
84 |
$timeHex = str_pad($uuidTime->toString(), 16, '0', STR_PAD_LEFT);
|
|
|
85 |
|
|
|
86 |
if (strlen($timeHex) !== 16) {
|
|
|
87 |
throw new TimeSourceException(sprintf(
|
|
|
88 |
'The generated time of \'%s\' is larger than expected',
|
|
|
89 |
$timeHex
|
|
|
90 |
));
|
|
|
91 |
}
|
|
|
92 |
|
|
|
93 |
$timeBytes = (string) hex2bin($timeHex);
|
|
|
94 |
|
|
|
95 |
return $timeBytes[4] . $timeBytes[5] . $timeBytes[6] . $timeBytes[7]
|
|
|
96 |
. $timeBytes[2] . $timeBytes[3]
|
|
|
97 |
. $timeBytes[0] . $timeBytes[1]
|
|
|
98 |
. pack('n*', $clockSeq)
|
|
|
99 |
. $node;
|
|
|
100 |
}
|
|
|
101 |
|
|
|
102 |
/**
|
|
|
103 |
* Uses the node provider given when constructing this instance to get
|
|
|
104 |
* the node ID (usually a MAC address)
|
|
|
105 |
*
|
|
|
106 |
* @param int|string|null $node A node value that may be used to override the node provider
|
|
|
107 |
*
|
|
|
108 |
* @return string 6-byte binary string representation of the node
|
|
|
109 |
*
|
|
|
110 |
* @throws InvalidArgumentException
|
|
|
111 |
*/
|
|
|
112 |
private function getValidNode(int | string | null $node): string
|
|
|
113 |
{
|
|
|
114 |
if ($node === null) {
|
|
|
115 |
$node = $this->nodeProvider->getNode();
|
|
|
116 |
}
|
|
|
117 |
|
|
|
118 |
// Convert the node to hex, if it is still an integer.
|
|
|
119 |
if (is_int($node)) {
|
|
|
120 |
$node = dechex($node);
|
|
|
121 |
}
|
|
|
122 |
|
|
|
123 |
if (!preg_match('/^[A-Fa-f0-9]+$/', (string) $node) || strlen((string) $node) > 12) {
|
|
|
124 |
throw new InvalidArgumentException('Invalid node value');
|
|
|
125 |
}
|
|
|
126 |
|
|
|
127 |
return (string) hex2bin(str_pad((string) $node, 12, '0', STR_PAD_LEFT));
|
|
|
128 |
}
|
|
|
129 |
}
|