Skip to content

Instantly share code, notes, and snippets.

@prograhammer
Last active November 15, 2016 15:54
Show Gist options
  • Save prograhammer/7f7e663f85447c9c6d74 to your computer and use it in GitHub Desktop.
Save prograhammer/7f7e663f85447c9c6d74 to your computer and use it in GitHub Desktop.
80-bit sequential unique identifier (as opposed to a 128-bit UUID)
<?php
/**
* Creates an 80-bit, binary(10) unique identifier.
*
* Smaller, so indexes fit better in memory/RAM and the hex representation
* is still relatively URL friendly. Sequential, so inserts/indexes faster.
* (see https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/)
*
* 4 bit worker id (supports 16 servers, from 0 to 15)
* 60 bit timestamp
* 16 bit random clock sequence
*
* Here are some useful MySQL calls:
* SELECT DATE_FORMAT(FROM_UNIXTIME(CONV(SUBSTR(HEX(people.id), 2, 15),16,10)/10000000),'%d %b %Y %T:%f') FROM people;
**/
class Snowflake
{
/**
* Time (in 100ns steps) between the start of the UTC and Unix epochs
* @var int
*/
const INTERVAL = 0x01b21dd213814000;
private $hex;
public function __construct($hex = null, $worker = "0")
{
if (empty($hex)) {
$hex = $this->makeHex($worker);
}
$this->hex = $hex;
}
public static function create($worker = "0")
{
return new Snowflake(null, $worker);
}
public static function createFromExisting($hex)
{
return new Snowflake($hex);
}
private function makeHex($worker)
{
// Convert decimal worker (0 to 15) to single digit hex (0 to F)
$worker = dechex($worker);
/** Get time since Gregorian calendar reform in 100ns intervals
* This is exceedingly difficult because of PHP's (and pack()'s)
* integer size limits.
* Note that this will never be more accurate than to the microsecond.
* You can also change the interval.
*/
// $timestamp = microtime(1) * 10000000 + static::INTERVAL;
$timestamp = microtime(1) * 10000000;
// Convert to a string representation
$timestamp = sprintf("%F", $timestamp);
//strip decimal point
preg_match("/^\d+/", $timestamp, $timestamp);
// And now to a 64-bit binary representation that begins with the worker id
$snowflake = base_convert($timestamp[0], 10, 16);
$snowflake = str_pad($snowflake, 16, dechex($worker), STR_PAD_LEFT);
// Generate a random clock sequence (2 bytes)
$snowflake .= bin2hex(mcrypt_create_iv(2, MCRYPT_DEV_URANDOM));
return $snowflake;
}
public function getHex(){
return $this->hex;
}
public function getDateTime($format = 'd M Y h:i:s A')
{
$timestamp = base_convert(substr($this->hex, 1, 15), 16, 10);
$timestamp = sprintf("%d", $timestamp);
// $timestamp = ($timestamp) / 10000000 - static::INTERVAL;
$timestamp = ($timestamp) / 10000000;
return date($format, $timestamp);
}
}
$uid = Snowflake::create();
echo "Hex: ". $uid->getHex()."\n"; // Hex: 003349028aca2aca3642
echo "DateTime: " . $uid->getDateTime(); // DateTime: 29 Sep 2015 08:05:07 PM
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment