1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the godruoyi/php-snowflake. |
5
|
|
|
* |
6
|
|
|
* (c) Godruoyi <[email protected]> |
7
|
|
|
* |
8
|
|
|
* This source file is subject to the MIT license that is bundled. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace Godruoyi\Snowflake; |
12
|
|
|
|
13
|
|
|
class Sonyflake extends Snowflake |
14
|
|
|
{ |
15
|
|
|
const MAX_TIMESTAMP_LENGTH = 39; |
16
|
|
|
|
17
|
|
|
const MAX_MACHINEID_LENGTH = 16; |
18
|
|
|
|
19
|
|
|
const MAX_SEQUENCE_LENGTH = 8; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* The machine ID. |
23
|
|
|
* |
24
|
|
|
* @var int |
25
|
|
|
*/ |
26
|
|
|
protected $machineid; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Build Sonyflake Instance. |
30
|
|
|
* |
31
|
|
|
* @param int $machineid machine ID 0 ~ 65535 (2^16)-1 |
32
|
|
|
*/ |
33
|
8 |
|
public function __construct(int $machineid = 0) |
34
|
|
|
{ |
35
|
8 |
|
$maxMachineID = -1 ^ (-1 << self::MAX_MACHINEID_LENGTH); |
36
|
|
|
|
37
|
8 |
|
$this->machineid = $machineid; |
38
|
8 |
|
if ($this->machineid < 0 || $this->machineid > $maxMachineID) { |
39
|
1 |
|
throw new \InvalidArgumentException("Invalid machine ID, must be between 0 ~ {$maxMachineID}."); |
40
|
|
|
} |
41
|
8 |
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Get Sonyflake id. |
45
|
|
|
* |
46
|
|
|
* @return string |
47
|
|
|
*/ |
48
|
2 |
|
public function id() |
49
|
|
|
{ |
50
|
2 |
|
$elapsedTime = $this->elapsedTime(); |
51
|
|
|
|
52
|
2 |
|
while (($sequence = $this->callResolver($elapsedTime)) > (-1 ^ (-1 << self::MAX_SEQUENCE_LENGTH))) { |
53
|
|
|
$elapsedTime2 = $this->elapsedTime(); |
54
|
|
|
// Get next timestamp |
55
|
|
|
while ($elapsedTime2 == $elapsedTime) { |
56
|
|
|
usleep(1); |
57
|
|
|
$elapsedTime2 = $this->elapsedTime(); |
58
|
|
|
} |
59
|
|
|
$elapsedTime = $elapsedTime2; |
60
|
|
|
} |
61
|
|
|
|
62
|
2 |
|
$machineidLeftMoveLength = self::MAX_SEQUENCE_LENGTH; |
63
|
2 |
|
$timestampLeftMoveLength = self::MAX_MACHINEID_LENGTH + $machineidLeftMoveLength; |
64
|
|
|
|
65
|
2 |
|
if ($elapsedTime > (-1 ^ (-1 << self::MAX_TIMESTAMP_LENGTH))) { |
66
|
|
|
// The lifetime (174 years). |
67
|
|
|
throw new \Exception('Exceeding the maximum life cycle of the algorithm.'); |
68
|
|
|
} |
69
|
|
|
|
70
|
2 |
|
return (string) ($elapsedTime << $timestampLeftMoveLength |
71
|
2 |
|
| ($this->machineid << $machineidLeftMoveLength) |
72
|
2 |
|
| ($sequence)); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Set start time (millisecond). |
77
|
|
|
*/ |
78
|
2 |
View Code Duplication |
public function setStartTimeStamp(int $startTime) |
|
|
|
|
79
|
|
|
{ |
80
|
2 |
|
$elapsedTime = floor(($this->getCurrentMicrotime() - $startTime) / 10) | 0; |
81
|
2 |
|
if ($elapsedTime < 0) { |
82
|
|
|
throw new \Exception('The start time cannot be greater than the current time'); |
83
|
|
|
} |
84
|
|
|
|
85
|
2 |
|
$maxTimeDiff = -1 ^ (-1 << self::MAX_TIMESTAMP_LENGTH); |
86
|
2 |
|
if ($elapsedTime > $maxTimeDiff) { |
87
|
1 |
|
throw new \Exception('Exceeding the maximum life cycle of the algorithm'); |
88
|
|
|
} |
89
|
|
|
|
90
|
1 |
|
$this->startTime = $startTime; |
91
|
|
|
|
92
|
1 |
|
return $this; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Parse snowflake id. |
97
|
|
|
*/ |
98
|
1 |
|
public function parseId(string $id, $transform = false): array |
99
|
|
|
{ |
100
|
1 |
|
$id = decbin($id); |
101
|
1 |
|
$length = self::MAX_SEQUENCE_LENGTH + self::MAX_MACHINEID_LENGTH; |
102
|
|
|
|
103
|
|
|
$data = [ |
104
|
1 |
|
'sequence' => substr($id, -1 * self::MAX_SEQUENCE_LENGTH), |
105
|
1 |
|
'machineid' => substr($id, -1 * $length, self::MAX_MACHINEID_LENGTH), |
106
|
1 |
|
'timestamp' => substr($id, 0, $length), |
107
|
|
|
]; |
108
|
|
|
|
109
|
|
|
return $transform ? array_map(function ($value) { |
110
|
1 |
|
return bindec($value); |
111
|
1 |
|
}, $data) : $data; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* The Elapsed Time. |
116
|
|
|
* |
117
|
|
|
* @return int |
118
|
|
|
*/ |
119
|
2 |
|
private function elapsedTime() |
120
|
|
|
{ |
121
|
2 |
|
return floor(($this->getCurrentMicrotime() - $this->getStartTimeStamp()) / 10) | 0; |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
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.