Files
yusheng-php/application/common/library/Snowflake.php
2025-12-26 11:47:14 +08:00

122 lines
2.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\common\library;
/**
* 雪花算法ID生成器
*/
class Snowflake
{
// 起始时间戳2023-01-01 00:00:00
const EPOCH = 1672531200000;
// 机器ID位数
const MACHINE_BITS = 10;
// 序列号位数
const SEQUENCE_BITS = 12;
// 最大机器ID
const MAX_MACHINE_ID = -1 ^ (-1 << self::MACHINE_BITS);
// 最大序列号
const MAX_SEQUENCE = -1 ^ (-1 << self::SEQUENCE_BITS);
// 机器ID左移位数
const MACHINE_SHIFT = self::SEQUENCE_BITS;
// 时间戳左移位数
const TIMESTAMP_SHIFT = self::SEQUENCE_BITS + self::MACHINE_BITS;
// 机器ID0-1023
private $machineId;
// 最后时间戳
private $lastTimestamp = -1;
// 序列号
private $sequence = 0;
/**
* 构造函数
* @param int $machineId 机器ID
*/
public function __construct($machineId = 1)
{
if ($machineId < 0 || $machineId > self::MAX_MACHINE_ID) {
throw new \InvalidArgumentException("机器ID必须在0-" . self::MAX_MACHINE_ID . "之间");
}
$this->machineId = $machineId;
}
/**
* 生成ID
* @return string
*/
public function generateId()
{
$timestamp = $this->getTimestamp();
if ($timestamp < $this->lastTimestamp) {
throw new \Exception("时钟回拨异常");
}
if ($timestamp == $this->lastTimestamp) {
$this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE;
if ($this->sequence == 0) {
$timestamp = $this->waitNextMillis($this->lastTimestamp);
}
} else {
$this->sequence = 0;
}
$this->lastTimestamp = $timestamp;
return (($timestamp - self::EPOCH) << self::TIMESTAMP_SHIFT) |
($this->machineId << self::MACHINE_SHIFT) |
$this->sequence;
}
/**
* 获取当前毫秒时间戳
* @return int
*/
private function getTimestamp()
{
return (int)(microtime(true) * 1000);
}
/**
* 等待下一毫秒
* @param int $lastTimestamp
* @return int
*/
private function waitNextMillis($lastTimestamp)
{
$timestamp = $this->getTimestamp();
while ($timestamp <= $lastTimestamp) {
usleep(100); // 休眠100微秒
$timestamp = $this->getTimestamp();
}
return $timestamp;
}
/**
* 解析ID
* @param string $id
* @return array
*/
public static function parseId($id)
{
$timestamp = ($id >> self::TIMESTAMP_SHIFT) + self::EPOCH;
$machineId = ($id >> self::MACHINE_SHIFT) & self::MAX_MACHINE_ID;
$sequence = $id & self::MAX_SEQUENCE;
return [
'timestamp' => $timestamp,
'machine_id' => $machineId,
'sequence' => $sequence,
'generate_time' => date('Y-m-d H:i:s', $timestamp / 1000)
];
}
}