2025-12-26 11:47:14 +08:00
|
|
|
|
<?php
|
2025-12-30 11:37:21 +08:00
|
|
|
|
// application/common/library/Snowflake.php
|
2025-12-26 18:46:06 +08:00
|
|
|
|
|
2025-12-26 11:47:14 +08:00
|
|
|
|
namespace app\common\library;
|
|
|
|
|
|
|
|
|
|
|
|
class Snowflake
|
|
|
|
|
|
{
|
2025-12-30 11:37:21 +08:00
|
|
|
|
// 起始时间戳(2023-01-01)
|
|
|
|
|
|
const EPOCH = 1672502400000;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
2025-12-30 11:37:21 +08:00
|
|
|
|
// 机器ID位数
|
|
|
|
|
|
const WORKER_ID_BITS = 5;
|
|
|
|
|
|
const DATACENTER_ID_BITS = 5;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
const SEQUENCE_BITS = 12;
|
|
|
|
|
|
|
2025-12-30 11:37:21 +08:00
|
|
|
|
// 最大值
|
|
|
|
|
|
const MAX_WORKER_ID = -1 ^ (-1 << self::WORKER_ID_BITS);
|
|
|
|
|
|
const MAX_DATACENTER_ID = -1 ^ (-1 << self::DATACENTER_ID_BITS);
|
|
|
|
|
|
const MAX_SEQUENCE = -1 ^ (-1 << self::SEQUENCE_BITS);
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
2025-12-30 11:37:21 +08:00
|
|
|
|
// 移位
|
2025-12-26 18:46:06 +08:00
|
|
|
|
const WORKER_ID_SHIFT = self::SEQUENCE_BITS;
|
2025-12-30 11:37:21 +08:00
|
|
|
|
const DATACENTER_ID_SHIFT = self::SEQUENCE_BITS + self::WORKER_ID_BITS;
|
|
|
|
|
|
const TIMESTAMP_LEFT_SHIFT = self::SEQUENCE_BITS + self::WORKER_ID_BITS + self::DATACENTER_ID_BITS;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
protected $workerId;
|
2025-12-30 11:37:21 +08:00
|
|
|
|
protected $datacenterId;
|
2025-12-26 18:46:06 +08:00
|
|
|
|
protected $sequence = 0;
|
|
|
|
|
|
protected $lastTimestamp = -1;
|
|
|
|
|
|
|
2025-12-30 11:37:21 +08:00
|
|
|
|
public function __construct($workerId = 1, $datacenterId = 1)
|
2025-12-26 11:47:14 +08:00
|
|
|
|
{
|
2025-12-30 11:37:21 +08:00
|
|
|
|
if ($workerId > self::MAX_WORKER_ID || $workerId < 0) {
|
|
|
|
|
|
throw new \Exception("worker Id can't be greater than " . self::MAX_WORKER_ID . " or less than 0");
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-30 11:37:21 +08:00
|
|
|
|
if ($datacenterId > self::MAX_DATACENTER_ID || $datacenterId < 0) {
|
|
|
|
|
|
throw new \Exception("datacenter Id can't be greater than " . self::MAX_DATACENTER_ID . " or less than 0");
|
2025-12-26 18:46:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-30 11:37:21 +08:00
|
|
|
|
$this->workerId = $workerId;
|
|
|
|
|
|
$this->datacenterId = $datacenterId;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
public function nextId()
|
2025-12-26 11:47:14 +08:00
|
|
|
|
{
|
2025-12-30 11:37:21 +08:00
|
|
|
|
$timestamp = $this->timeGen();
|
2025-12-26 11:47:14 +08:00
|
|
|
|
|
|
|
|
|
|
if ($timestamp < $this->lastTimestamp) {
|
2025-12-26 18:46:06 +08:00
|
|
|
|
$diff = $this->lastTimestamp - $timestamp;
|
2025-12-30 11:37:21 +08:00
|
|
|
|
throw new \Exception("Clock moved backwards. Refusing to generate id for {$diff} milliseconds");
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 18:46:06 +08:00
|
|
|
|
if ($this->lastTimestamp == $timestamp) {
|
2025-12-30 11:37:21 +08:00
|
|
|
|
$this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
if ($this->sequence == 0) {
|
2025-12-30 11:37:21 +08:00
|
|
|
|
$timestamp = $this->tilNextMillis($this->lastTimestamp);
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$this->sequence = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$this->lastTimestamp = $timestamp;
|
|
|
|
|
|
|
2025-12-30 11:37:21 +08:00
|
|
|
|
return (($timestamp - self::EPOCH) << self::TIMESTAMP_LEFT_SHIFT) |
|
|
|
|
|
|
($this->datacenterId << self::DATACENTER_ID_SHIFT) |
|
|
|
|
|
|
($this->workerId << self::WORKER_ID_SHIFT) |
|
|
|
|
|
|
$this->sequence;
|
2025-12-26 18:46:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-30 11:37:21 +08:00
|
|
|
|
protected function tilNextMillis($lastTimestamp)
|
2025-12-26 18:46:06 +08:00
|
|
|
|
{
|
2025-12-30 11:37:21 +08:00
|
|
|
|
$timestamp = $this->timeGen();
|
|
|
|
|
|
while ($timestamp <= $lastTimestamp) {
|
|
|
|
|
|
$timestamp = $this->timeGen();
|
2025-12-26 18:46:06 +08:00
|
|
|
|
}
|
2025-12-30 11:37:21 +08:00
|
|
|
|
return $timestamp;
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-30 11:37:21 +08:00
|
|
|
|
protected function timeGen()
|
2025-12-26 11:47:14 +08:00
|
|
|
|
{
|
2025-12-30 11:37:21 +08:00
|
|
|
|
return floor(microtime(true) * 1000);
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-30 11:37:21 +08:00
|
|
|
|
* 解析雪花ID
|
|
|
|
|
|
* @param int $id
|
|
|
|
|
|
* @return array
|
2025-12-26 11:47:14 +08:00
|
|
|
|
*/
|
2025-12-30 11:37:21 +08:00
|
|
|
|
public static function parseId($id)
|
2025-12-26 11:47:14 +08:00
|
|
|
|
{
|
2025-12-30 11:37:21 +08:00
|
|
|
|
$binary = decbin($id);
|
|
|
|
|
|
$binary = str_pad($binary, 64, '0', STR_PAD_LEFT);
|
|
|
|
|
|
|
|
|
|
|
|
$timestamp = bindec(substr($binary, 0, 42));
|
|
|
|
|
|
$timestamp = $timestamp + self::EPOCH;
|
|
|
|
|
|
|
|
|
|
|
|
$datacenterId = bindec(substr($binary, 42, 5));
|
|
|
|
|
|
$workerId = bindec(substr($binary, 47, 5));
|
|
|
|
|
|
$sequence = bindec(substr($binary, 52, 12));
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
'timestamp' => $timestamp,
|
|
|
|
|
|
'datacenterId' => $datacenterId,
|
|
|
|
|
|
'workerId' => $workerId,
|
|
|
|
|
|
'sequence' => $sequence
|
|
|
|
|
|
];
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-30 11:37:21 +08:00
|
|
|
|
* 生成ID(单例模式)
|
|
|
|
|
|
* @return int
|
2025-12-26 11:47:14 +08:00
|
|
|
|
*/
|
2025-12-30 11:37:21 +08:00
|
|
|
|
public static function generate()
|
2025-12-26 11:47:14 +08:00
|
|
|
|
{
|
2025-12-30 11:37:21 +08:00
|
|
|
|
static $instance = null;
|
|
|
|
|
|
if (!$instance) {
|
|
|
|
|
|
// 从配置获取workerId和datacenterId
|
|
|
|
|
|
$workerId = config('snowflake.worker_id') ?: 1;
|
|
|
|
|
|
$datacenterId = config('snowflake.datacenter_id') ?: 1;
|
|
|
|
|
|
$instance = new self($workerId, $datacenterId);
|
|
|
|
|
|
}
|
|
|
|
|
|
return $instance->nextId();
|
2025-12-26 11:47:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|