Issues (31)

src/UniqueId.php (3 issues)

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()
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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
0 ignored issues
show
Documentation Bug introduced by
The doc comment &array at position 0 could not be parsed: Unknown type name '&' at position 0 in &array.
Loading history...
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