Created
March 1, 2019 12:27
-
-
Save alksily/f6ffbae7e7eaf7beecbad5a9086667f7 to your computer and use it in GitHub Desktop.
Snowflake
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
define('EPOCH', 1414213562373); | |
define('NUMWORKERBITS', 10); | |
define('NUMSEQUENCEBITS', 12); | |
define('MAXWORKERID', (-1 ^ (-1 << NUMWORKERBITS))); | |
define('MAXSEQUENCE', (-1 ^ (-1 << NUMSEQUENCEBITS))); | |
class Snowflake | |
{ | |
private $_lastTimestamp; | |
private $_sequence = 0; | |
private $_workerId = 1; | |
public function __construct($workerId) | |
{ | |
if (($workerId < 0) || ($workerId > MAXWORKERID)) { | |
return null; | |
} | |
$this->workerId = $workerId; | |
} | |
public function next() | |
{ | |
$ts = $this->timestamp(); | |
if ($ts == $this->_lastTimestamp) { | |
$this->_sequence = ($this->_sequence + 1) & MAXSEQUENCE; | |
if ($this->_sequence == 0) { | |
$ts = $this->waitNextMilli($ts); | |
} | |
} else { | |
$this->_sequence = 0; | |
} | |
if ($ts < $this->_lastTimestamp) { | |
return 0; | |
} | |
$this->_lastTimestamp = $ts; | |
return $this->pack(); | |
} | |
private function pack() | |
{ | |
return ($this->_lastTimestamp << (NUMWORKERBITS + NUMSEQUENCEBITS)) | ($this->_workerId << NUMSEQUENCEBITS) | $this->_sequence; | |
} | |
private function waitNextMilli($ts) | |
{ | |
if ($ts = $this->_lastTimestamp) { | |
sleep(0.1); | |
$ts = $this->timestamp(); | |
} | |
return $ts; | |
} | |
private function timestamp() | |
{ | |
return $this->millitime() - EPOCH; | |
} | |
private function millitime() | |
{ | |
$microtime = microtime(); | |
$comps = explode(' ', $microtime); | |
// Note: Using a string here to prevent loss of precision | |
// in case of "overflow" (PHP converts it to a double) | |
return sprintf('%d%03d', $comps[1], $comps[0] * 1000); | |
} | |
} | |
$snowflake = new Snowflake(1); | |
$a = $snowflake->next(); | |
usleep(rand(1000, 6000)); | |
$b = $snowflake->next(); | |
usleep(rand(1000, 6000)); | |
$c = $snowflake->next(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment