Completed
Push — master ( 326182...c37a35 )
by Xu
05:59 queued 26s
created

Snowflake::waitNextMilli()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @link http://www.tintsoft.com/
4
 * @copyright Copyright (c) 2012 TintSoft Technology Co. Ltd.
5
 * @license http://www.tintsoft.com/license/
6
 */
7
8
namespace yuncms\base;
9
10
use yii\base\Component;
11
use yii\base\Exception;
12
13
/**
14
 * Class Snowflake
15
 *
16
 * @author Tongle Xu <[email protected]>
17
 * @since 3.0
18
 */
19
class Snowflake extends Component
20
{
21
    /**
22
     * @var int 开始时间截
23
     */
24
    public $epoch = 1414213562373;
25
26
    /**
27
     * @var int 工作机器ID(0~31)
28
     */
29
    public $workerId;
30
31
    /**
32
     * @var int 数据中心ID(0~31)
33
     */
34
    public $dataCenterId;
35
36
    /**
37
     * @var int 上次生成ID的时间截
38
     */
39
    private $_lastTimestamp;
40
41
    /**
42
     * @var int 毫秒内序列(0~4095)
43
     */
44
    private $_sequence = 0;
45
46
    const WORKER_BITS = 5;
47
    const DATA_CENTER_BITS = 5;
48
    const MAX_WORKER_ID = (-1 ^ (-1 << self::WORKER_BITS));
49
    const MAX_DATA_CENTER_ID = (-1 ^ (-1 << self::DATA_CENTER_BITS));
50
    const SEQUENCE_BITS = 12;
51
    const WORKER_ID_SHIFT = self::SEQUENCE_BITS;
52
    const DATA_CENTER_ID_SHIFT = self::SEQUENCE_BITS + self::WORKER_BITS;
53
    const TIMESTAMP_LEFT_SHIFT = self::SEQUENCE_BITS + self::WORKER_BITS + self::DATA_CENTER_BITS;
54
    const SEQUENCE_MASK = (-1 ^ (-1 << self::SEQUENCE_BITS));
55
56
    /**
57
     * 初始化
58
     * @throws Exception
59
     */
60
    public function init()
61
    {
62
        parent::init();
63
        if ($this->workerId > self::MAX_WORKER_ID || $this->workerId < 0) {
64
            throw new Exception(sprintf("worker Id can't be greater than %d or less than 0",self::MAX_WORKER_ID));
65
        }
66
        if ($this->dataCenterId > self::MAX_DATA_CENTER_ID || $this->dataCenterId < 0) {
67
            throw new Exception(sprintf("dataCenterId can't be greater than %d or less than 0",self::MAX_DATA_CENTER_ID));
68
        }
69
    }
70
71
    /**
72
     * 获得下一个ID (该方法是线程安全的)
73
     * @return int
74
     * @throws Exception
75
     */
76
    public function next()
77
    {
78
        $timestamp = $this->milliTime();
79
        if ($timestamp < $this->_lastTimestamp) {
80
            throw new Exception(sprintf("Clock moved backwards.  Refusing to generate id for %d milliseconds", $this->_lastTimestamp - $timestamp));
81
        }
82
        if ($this->_lastTimestamp == $timestamp) {
83
            $this->_sequence = ($this->_sequence + 1) & self::SEQUENCE_MASK;
84
            if ($this->_sequence == 0) {
85
                $timestamp = $this->waitNextMilli($this->_lastTimestamp);
86
            }
87
        } else {
88
            $this->_sequence = 0;
89
        }
90
        $this->_lastTimestamp = $timestamp;
91
        return (($timestamp - $this->epoch) << self::TIMESTAMP_LEFT_SHIFT)
92
            | ($this->dataCenterId << self::DATA_CENTER_ID_SHIFT)
93
            | ($this->workerId << self::WORKER_ID_SHIFT)
94
            | $this->_sequence;
95
    }
96
97
    /**
98
     * 阻塞到下一个毫秒,直到获得新的时间戳
99
     * @param int $lastTimestamp 上次生成ID的时间截
100
     * @return int 当前时间戳
101
     */
102
    protected function waitNextMilli($lastTimestamp)
103
    {
104
        $timestamp = $this->milliTime();
105
        while ($timestamp <= $lastTimestamp) {
106
            $timestamp = $this->milliTime();
107
        }
108
        return $timestamp;
109
    }
110
111
    /**
112
     * 返回以毫秒为单位的当前时间
113
     * @return int 当前时间(毫秒)
114
     */
115
    protected function milliTime()
116
    {
117
        $microTime = microtime();
118
        $comps = explode(' ', $microTime);
119
        // Note: Using a string here to prevent loss of precision
120
        // in case of "overflow" (PHP converts it to a double)
121
        return sprintf('%d%03d', $comps[1], $comps[0] * 1000);
0 ignored issues
show
Bug Best Practice introduced by
The expression return sprintf('%d%03d',...s[1], $comps[0] * 1000) returns the type string which is incompatible with the documented return type integer.
Loading history...
122
    }
123
}