1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ODM\MongoDB\Id; |
6
|
|
|
|
7
|
|
|
use Doctrine\ODM\MongoDB\DocumentManager; |
8
|
|
|
use Exception; |
9
|
|
|
use const E_USER_DEPRECATED; |
10
|
|
|
use function chr; |
11
|
|
|
use function hexdec; |
12
|
|
|
use function mt_rand; |
13
|
|
|
use function php_uname; |
14
|
|
|
use function preg_match; |
15
|
|
|
use function sha1; |
16
|
|
|
use function sprintf; |
17
|
|
|
use function str_replace; |
18
|
|
|
use function strlen; |
19
|
|
|
use function substr; |
20
|
|
|
use function trigger_error; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Generates UUIDs. |
24
|
|
|
* |
25
|
|
|
* @final |
26
|
|
|
*/ |
27
|
|
|
class UuidGenerator extends AbstractIdGenerator |
28
|
|
|
{ |
29
|
|
|
/** |
30
|
|
|
* A unique environment value to salt each UUID with. |
31
|
|
|
* |
32
|
|
|
* @var string |
33
|
|
|
*/ |
34
|
|
|
protected $salt = null; |
35
|
|
|
|
36
|
6 |
View Code Duplication |
public function __construct() |
|
|
|
|
37
|
|
|
{ |
38
|
6 |
|
if (self::class === static::class) { |
39
|
6 |
|
return; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
@trigger_error(sprintf('The class "%s" extends "%s" which will be final in MongoDB ODM 2.0.', static::class, self::class), E_USER_DEPRECATED); |
|
|
|
|
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Used to set the salt that will be applied to each id |
47
|
|
|
*/ |
48
|
2 |
|
public function setSalt(string $salt) : void |
49
|
|
|
{ |
50
|
2 |
|
$this->salt = $salt; |
51
|
2 |
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Returns the current salt value |
55
|
|
|
* |
56
|
|
|
* @return string $salt The current salt |
57
|
|
|
*/ |
58
|
1 |
|
public function getSalt() : string |
59
|
|
|
{ |
60
|
1 |
|
return $this->salt; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Checks that a given string is a valid uuid. |
65
|
|
|
*/ |
66
|
2 |
|
public function isValid(string $uuid) : bool |
67
|
|
|
{ |
68
|
2 |
|
return preg_match('/^\{?[0-9a-f]{8}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?[0-9a-f]{12}\}?$/i', $uuid) |
69
|
2 |
|
=== 1; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Generates a new UUID |
74
|
|
|
* |
75
|
|
|
* @param DocumentManager $dm Not used. |
76
|
|
|
* @param object $document Not used. |
77
|
|
|
* |
78
|
|
|
* @return string UUID |
79
|
|
|
* |
80
|
|
|
* @throws Exception |
81
|
|
|
*/ |
82
|
2 |
|
public function generate(DocumentManager $dm, object $document) |
83
|
|
|
{ |
84
|
2 |
|
$uuid = $this->generateV4(); |
85
|
2 |
|
return $this->generateV5($uuid, $this->salt ?: php_uname('n')); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Generates a v4 UUID |
90
|
|
|
*/ |
91
|
4 |
|
public function generateV4() : string |
92
|
|
|
{ |
93
|
4 |
|
return sprintf( |
94
|
4 |
|
'%04x%04x%04x%04x%04x%04x%04x%04x', |
95
|
|
|
// 32 bits for "time_low" |
96
|
4 |
|
mt_rand(0, 0xffff), |
97
|
4 |
|
mt_rand(0, 0xffff), |
98
|
|
|
// 16 bits for "time_mid" |
99
|
4 |
|
mt_rand(0, 0xffff), |
100
|
|
|
// 16 bits for "time_hi_and_version", |
101
|
|
|
// four most significant bits holds version number 4 |
102
|
4 |
|
mt_rand(0, 0x0fff) | 0x4000, |
103
|
|
|
// 16 bits, 8 bits for "clk_seq_hi_res", |
104
|
|
|
// 8 bits for "clk_seq_low", |
105
|
|
|
// two most significant bits holds zero and one for variant DCE1.1 |
106
|
4 |
|
mt_rand(0, 0x3fff) | 0x8000, |
107
|
|
|
// 48 bits for "node" |
108
|
4 |
|
mt_rand(0, 0xffff), |
109
|
4 |
|
mt_rand(0, 0xffff), |
110
|
4 |
|
mt_rand(0, 0xffff) |
111
|
|
|
); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Generates a v5 UUID |
116
|
|
|
* |
117
|
|
|
* @throws Exception When the provided namespace is invalid. |
118
|
|
|
*/ |
119
|
2 |
|
public function generateV5(string $namespace, string $salt) : string |
120
|
|
|
{ |
121
|
2 |
|
if (! $this->isValid($namespace)) { |
122
|
|
|
throw new Exception('Provided $namespace is invalid: ' . $namespace); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
// Get hexadecimal components of namespace |
126
|
2 |
|
$nhex = str_replace(['-', '{', '}'], '', $namespace); |
127
|
|
|
|
128
|
|
|
// Binary Value |
129
|
2 |
|
$nstr = ''; |
130
|
|
|
|
131
|
|
|
// Convert Namespace UUID to bits |
132
|
2 |
|
for ($i = 0; $i < strlen($nhex); $i += 2) { |
133
|
2 |
|
$nstr .= chr((int) hexdec($nhex[$i] . $nhex[$i + 1])); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
// Calculate hash value |
137
|
2 |
|
$hash = sha1($nstr . $salt); |
138
|
|
|
|
139
|
2 |
|
return sprintf( |
140
|
2 |
|
'%08s%04s%04x%04x%12s', |
141
|
|
|
// 32 bits for "time_low" |
142
|
2 |
|
substr($hash, 0, 8), |
143
|
|
|
// 16 bits for "time_mid" |
144
|
2 |
|
substr($hash, 8, 4), |
145
|
|
|
// 16 bits for "time_hi_and_version", |
146
|
|
|
// four most significant bits holds version number 3 |
147
|
2 |
|
(hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x3000, |
148
|
|
|
// 16 bits, 8 bits for "clk_seq_hi_res", |
149
|
|
|
// 8 bits for "clk_seq_low", |
150
|
|
|
// two most significant bits holds zero and one for variant DCE1.1 |
151
|
2 |
|
(hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000, |
152
|
|
|
// 48 bits for "node" |
153
|
2 |
|
substr($hash, 20, 12) |
154
|
|
|
); |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.