Skip to content

Instantly share code, notes, and snippets.

@MilesPong
Created April 4, 2018 03:29
Show Gist options
  • Save MilesPong/080009075f82f9606fda976d24b0d8d5 to your computer and use it in GitHub Desktop.
Save MilesPong/080009075f82f9606fda976d24b0d8d5 to your computer and use it in GitHub Desktop.
Snowflake algo sample (PHP)
<?php
use Exception;
/**
* Class Snowflake
*
* @see https://github.com/twitter/snowflake
*/
class Snowflake
{
/**
*
*/
const SIGN_BIT = 1;
/**
*
*/
const TIMESTAMP_BIT = 41;
/**
*
*/
const DATACENTER_BIT = 5;
/**
*
*/
const MACHINE_BIT = 5;
/**
*
*/
const SEQUENCE_BIT = 12;
/**
*
*/
const MAX_TIMESTAMP_DEC = (1 << self::TIMESTAMP_BIT) - 1;
/**
*
*/
const MAX_DATACENTER_DEC = (1 << self::DATACENTER_BIT) - 1;
/**
*
*/
const MAX_MACHINE_DEC = (1 << self::MACHINE_BIT) - 1;
/**
*
*/
const MAX_SEQUENCE_DEC = (1 << self::SEQUENCE_BIT) - 1;
/**
*
*/
const TIMESTAMP_OFFSET = self::DATACENTER_BIT + self::MACHINE_BIT + self::SEQUENCE_BIT;
/**
*
*/
const DATACENTER_OFFSET = self::MACHINE_BIT + self::SEQUENCE_BIT;
/**
*
*/
const MACHINE_OFFSET = self::SEQUENCE_BIT;
/**
*
*/
const EPOCH_TIME = 1514260211335; // 2017-12-26 11:50
/**
* @var int
*/
protected $datacenterId;
/**
* @var int
*/
protected $machineId;
/**
* @var
*/
private $_lastTimestamp;
/**
* @var int
*/
private $_sequence = 0;
/**
* Snowflake constructor.
* @param int $datacenterId
* @param int $machineId
* @throws \Exception
*/
public function __construct($datacenterId = 0, $machineId = 0)
{
$this->datacenterId = $datacenterId;
$this->machineId = $machineId;
$this->validate();
}
/**
* Simple validation.
*
* @throws \Exception
*/
protected function validate()
{
if ($this->datacenterId > self::MAX_DATACENTER_DEC || $this->datacenterId < 0) {
throw new Exception('Illegal datacenter id.');
}
if ($this->machineId > self::MAX_MACHINE_DEC || $this->machineId < 0) {
throw new Exception('Illegal machine id.');
}
}
/**
* Create next ID.
*
* @return int
* @throws \Exception
*/
public function next()
{
$timestamp = $this->getMilliseconds();
if ($timestamp < $this->_lastTimestamp) {
throw new Exception('Clock error.');
}
if ($timestamp == $this->_lastTimestamp) {
$this->_sequence = ++$this->_sequence & self::MAX_SEQUENCE_DEC;
if ($this->_sequence == 0) {
$timestamp = $this->waitTillNextMS($this->_lastTimestamp);
}
} else {
$this->_sequence = 0;
}
$this->_lastTimestamp = $timestamp;
return ($timestamp - self::EPOCH_TIME) << self::TIMESTAMP_OFFSET
| $this->datacenterId << self::DATACENTER_OFFSET
| $this->machineId << self::MACHINE_OFFSET
| $this->_sequence;
}
/**
* @return float
*/
protected function getMilliseconds()
{
return floor(microtime(true) * 1000);
}
/**
* @param $lastTimestamp
* @return float
*/
protected function waitTillNextMS($lastTimestamp)
{
do {
$timestamp = $this->getMilliseconds();
} while ($timestamp <= $lastTimestamp);
return $timestamp;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment