Issues (31)

src/UniqueId.php (1 issue)

1
<?php
2
namespace tinymeng\tools;
3
/**
4
 *
5
 * STRUCTURE
6
 * ================================================================================
7
 *
8
 * 0 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx x xxxxx xxxxx xxxx xxxxxxxx
9
 * - ---------------------------------------------- ----- ----- -----------
10
 * Escaped Time (in millisecond)                  Center Node Random
11
 * 1 41 bits                                        5     5     12 bits
12
 * 0~69 (years)                                   0~32  0~32  0~4095
13
 *
14
 *
15
 * Center
16
 * 0 00000000 00000000 00000000 00000000 00000000 0 11111 00000 0000 00000000
17
 * 00000000 00000000 00000000 00000000 00000000 00111110 00000000 00000000
18
 * 00       00       00       00       00       3E       00       00
19
 *
20
 * Node
21
 * 0 00000000 00000000 00000000 00000000 00000000 0 00000 11111 0000 00000000
22
 * 00000000 00000000 00000000 00000000 00000000 00000001 11110000 00000000
23
 * 00       00       00       00       00       01       F0       00
24
 *
25
 * Escaped Time
26
 * 0 11111111 11111111 11111111 11111111 11111111 1 00000 00000 0000 00000000
27
 * 01111111 11111111 11111111 11111111 11111111 11000000 00000000 00000000
28
 * 7F       FF       FF       FF       FF       C0       00       00
29
 *
30
 * Random
31
 * 0 00000000 00000000 00000000 00000000 00000000 0 00000 00000 1111 11111111
32
 * 00000000 00000000 00000000 00000000 00000000 00000000 00001111 11111111
33
 * 00       00       00       00       00       00       0F       FF
34
 *
35
 * php -r 'echo dechex( 0b00111111 );'
36
 */
37
class UniqueId{
38
39
40
    protected static $g_cInstanceDId;
41
42
    /**
43
     *	Offset from Unix Epoch
44
     *
45
     *	Unix Epoch :	January 1, 1970 00:00:00 GMT
46
     *	Epoch Offset :	November 7, 2016 00:00:00 GMT
47
     */
48
    CONST EPOCH_OFFSET = 1478476800000;
49
50
    /**
51
     * __construct
52
     */
53
    public function __construct(){}
54
55
    /**
56
     * __destruct
57
     */
58
    public function __destruct(){}
59
60
    /**
61
     * getInstance
62
     * @return self
63
     */
64
    static function getInstance()
65
    {
66
        if ( is_null( self::$g_cInstanceDId ) || ! isset( self::$g_cInstanceDId ) )
67
        {
68
            self::$g_cInstanceDId = new self();
69
        }
70
        return self::$g_cInstanceDId;
71
    }
72
73
74
    /**
75
     *	Generate an unique id
76
     *
77
     *	@param $nCenter int	data center id ( 0 ~ 31 )
78
     *	@param $nNode int	data node id ( 0 ~ 31 )
79
     *	@param $sSource string	source string for calculating crc32 hash value
80
     *	@param $arrData &array	details about the id
81
     *	@return int(64)	id
82
     */
83
    public function createId( $nCenter, $nNode, $sSource = null, & $arrData = null )
84
    {
85
        if ( ! $this->isValidCenterId( $nCenter ) )
86
        {
87
            return null;
88
        }
89
        if ( ! $this->isValidNodeId( $nNode ) )
90
        {
91
            return null;
92
        }
93
94
        //	...
95
        $nRet	= 0;
0 ignored issues
show
The assignment to $nRet is dead and can be removed.
Loading history...
96
        $nTime	= $this->getEscapedTime();
97
98
        if ( is_string( $sSource ) && strlen( $sSource ) > 0 )
99
        {
100
            //	use crc32 hash value instead of rand
101
            $nRand	= crc32( $sSource );
102
        }
103
        else
104
        {
105
            //	0 ~ 4095
106
            $nRand	= rand( 0, 0xFFF );
107
        }
108
109
        //	...
110
        $nCenterV	= ( ( $nCenter << 17 ) & 0x00000000003E0000 );
111
        $nNodeV		= ( ( $nNode   << 12 ) & 0x000000000001F000 );
112
        $nTimeV		= ( ( $nTime   << 22 ) & 0x7FFFFFFFFFC00000 );
113
        $nRandV		= ( ( $nRand   << 0  ) & 0x0000000000000FFF );
114
115
        $nRet		= ( $nCenterV + $nNodeV + $nTimeV + $nRandV );
116
117
        //	...
118
        if ( ! is_null( $arrData ) )
119
        {
120
            $arrData =
121
                [
122
                    'center'	=> $nCenter,
123
                    'node'		=> $nNode,
124
                    'time'		=> $nTime,
125
                    'rand'		=> $nRandV,
126
                ];
127
        }
128
129
        return intval( $nRet );
130
    }
131
132
    /**
133
     *	Parse an unique id
134
     *
135
     *	@param $nId int		64 bits unique id
136
     *	@return array		details about the id
137
     */
138
    public function parseId( $nId )
139
    {
140
        if ( ! is_numeric( $nId ) || $nId <= 0 )
141
        {
142
            return null;
143
        }
144
145
        //	...
146
        $nCenter	= ( ( $nId & 0x00000000003E0000 ) >> 17 );
147
        $nNode		= ( ( $nId & 0x000000000001F000 ) >> 12 );
148
        $nTime		= ( ( $nId & 0x7FFFFFFFFFC00000 ) >> 22 );
149
        $nRand		= ( ( $nId & 0x0000000000000FFF ) >> 0  );
150
151
        return
152
            [
153
                'center'	=> $nCenter,
154
                'node'		=> $nNode,
155
                'time'		=> $nTime,
156
                'rand'		=> $nRand,
157
            ];
158
    }
159
160
    /**
161
     *	Verify whether the id is valid
162
     *
163
     *	@param $nVal int	64 bits unique id
164
     *	@return boolean		true or false
165
     */
166
    public function isValidId( $nVal )
167
    {
168
        $bRet	= false;
169
        $arrD	= $this->parseId( $nVal );
170
        if ( is_array( $arrD ) &&
171
            array_key_exists( 'center', $arrD ) &&
172
            array_key_exists( 'node', $arrD ) &&
173
            array_key_exists( 'time', $arrD ) &&
174
            array_key_exists( 'rand', $arrD ) )
175
        {
176
            if ( $this->isValidCenterId( $arrD[ 'center' ] ) &&
177
                $this->isValidNodeId( $arrD[ 'node' ] ) &&
178
                $this->isValidTime( $arrD[ 'time' ] ) &&
179
                $this->isValidRand( $arrD[ 'rand' ] ) )
180
            {
181
                $bRet = true;
182
            }
183
        }
184
185
        return $bRet;
186
    }
187
188
189
    /**
190
     * @param $nVal
191
     * @return bool
192
     */
193
    public function isValidCenterId( $nVal )
194
    {
195
        return is_numeric( $nVal ) && ( $nVal >= 0 ) && ( $nVal <= 31 );
196
    }
197
198
    /**
199
     * @param $nVal
200
     * @return bool
201
     */
202
    public function isValidNodeId( $nVal )
203
    {
204
        return is_numeric( $nVal ) && ( $nVal >= 0 ) && ( $nVal <= 31 );
205
    }
206
207
    /**
208
     * @param $nVal
209
     * @return bool
210
     */
211
    public function isValidTime( $nVal )
212
    {
213
        return is_numeric( $nVal ) && ( $nVal >= 0 );
214
    }
215
216
    /**
217
     * @param $nVal
218
     * @return bool
219
     */
220
    public function isValidRand( $nVal )
221
    {
222
        return is_numeric( $nVal ) && ( $nVal >= 0 ) && ( $nVal <= 0xFFF );
223
    }
224
225
226
    /**
227
     *	Get UNIX timestamp in millisecond
228
     *
229
     *	@return false|float    Timestamp in millisecond, for example: 1501780592275
230
     */
231
    public function getUnixTimestamp()
232
    {
233
        return floor( microtime( true ) * 1000 );
234
    }
235
236
    /**
237
     *	Get escaped time in millisecond
238
     *
239
     *	@return int	time in millisecond
240
     */
241
    public function getEscapedTime()
242
    {
243
        return intval( $this->getUnixTimestamp() - self::EPOCH_OFFSET );
244
    }
245
}
246