SnowFlake::nextMillisecond()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: yiranzai
5
 * Date: 19-3-23
6
 * Time: 下午3:56
7
 */
8
9
namespace Yiranzai\SnowFlake;
10
11
/**
12
 * Class SnowFlake
13
 * @package Yiranzai\SnowFlake
14
 */
15
final class SnowFlake
16
{
17
    // 41bit timestamp + 5bit dataCenter + 5bit worker + 12bit
18
    public const LENGTH = 2 << 5;
19
    public const TIMESTAMP_LENGTH = 41;
20
    public const DATA_CENTER_LENGTH = 5;
21
    public const WORKER_LENGTH = 5;
22
    public const SEQUENCE_LENGTH = 12;
23
24
    public const WORKER_LEFT_SHIFT = 12;
25
    public const DATA_CENTER_LEFT_SHIFT = 17;
26
    public const TIMESTAMP_LEFT_SHIFT = 22;
27
28
    public const MAX_SEQUENCE = (2 << 11) - 1;
29
30
    /**
31
     * 最后一次的时间戳
32
     *
33
     * @var int
34
     */
35
    private static $lastTimestamp = 0;
36
    /**
37
     * @var int
38
     */
39
    private static $lastSequence = 0;
40
    /**
41
     * @var int
42
     */
43
    private static $baseTimestamp = 1553328109824;
44
45
    /**
46
     * @param int $dataCenterID
47
     * @param int $workerID
48
     * @return int
49
     */
50 3
    public static function next($dataCenterID = 0, $workerID = 0): int
51
    {
52
53 3
        $timestamp = self::generateTimestamp();
54
55 3
        if (self::$lastTimestamp === $timestamp) {
56 3
            ++self::$lastSequence;
57 3
            if (self::$lastSequence > self::MAX_SEQUENCE) {
58
                $timestamp          = self::nextMillisecond();
59 3
                self::$lastSequence = 0;
60
            }
61
        } else {
62 3
            self::$lastSequence = 0;
63
        }
64 3
        self::$lastTimestamp = $timestamp;
65
66 3
        $snowFlakeId = (($timestamp - self::$baseTimestamp) << self::TIMESTAMP_LEFT_SHIFT)
67 3
            | ($dataCenterID << self::DATA_CENTER_LEFT_SHIFT)
68 3
            | ($workerID << self::WORKER_LEFT_SHIFT)
69 3
            | self::$lastSequence;
70 3
        return $snowFlakeId;
71
    }
72
73
    /**
74
     * @param $snowFlakeId
75
     * @return Node
76
     */
77 3
    public static function analysis($snowFlakeId): Node
78
    {
79 3
        $Binary             = str_pad(decbin($snowFlakeId), self::LENGTH, '0', STR_PAD_LEFT);
80 3
        $node               = new Node();
81 3
        $node->timestamp    = bindec(substr($Binary, 0, self::TIMESTAMP_LENGTH))
82 3
            + self::$baseTimestamp;
83 3
        $node->dataCenterID = bindec(substr(
84 3
            $Binary,
85 3
            self::TIMESTAMP_LENGTH + 1,
86 3
            self::DATA_CENTER_LENGTH
87
        ));
88 3
        $node->workerID     = bindec(substr(
89 3
            $Binary,
90 3
            self::LENGTH - self::DATA_CENTER_LEFT_SHIFT,
91 3
            self::WORKER_LENGTH
92
        ));
93 3
        $node->sequence     = bindec(substr($Binary, -self::SEQUENCE_LENGTH));
94 3
        return $node;
95
    }
96
97
    /**
98
     * @return int
99
     */
100 3
    private static function nextMillisecond(): int
101
    {
102 3
        $timestamp = self::generateTimestamp();
103 3
        while ($timestamp <= self::$lastTimestamp) {
104 3
            $timestamp = self::generateTimestamp();
105
        }
106 3
        return $timestamp;
107
    }
108
109
    /**
110
     * @return int
111
     */
112 3
    private static function generateTimestamp(): int
113
    {
114 3
        return (int)floor(microtime(true) * 1000);
115
    }
116
117
    /**
118
     * just test phpunit
119
     *
120
     * @param int $dataCenterID
121
     * @param int $workerID
122
     * @return int
123
     */
124 3
    public static function test($dataCenterID = 0, $workerID = 0): int
125
    {
126 3
        $timestamp = self::generateTimestamp();
127
128 3
        if (self::$lastTimestamp === $timestamp) {
129 3
            ++self::$lastSequence;
130 3
            if (self::$lastSequence > 10) {
131 3
                $timestamp          = self::nextMillisecond();
132 3
                self::$lastSequence = 0;
133
            }
134
        } else {
135 3
            self::$lastSequence = 0;
136
        }
137 3
        self::$lastTimestamp = $timestamp;
138
139 3
        $snowFlakeId = (($timestamp - self::$baseTimestamp) << self::TIMESTAMP_LEFT_SHIFT)
140 3
            | ($dataCenterID << self::DATA_CENTER_LEFT_SHIFT)
141 3
            | ($workerID << self::WORKER_LEFT_SHIFT)
142 3
            | self::$lastSequence;
143 3
        return $snowFlakeId;
144
    }
145
}
146