Files
yusheng-php/application/common/library/Snowflake.php
2025-12-30 11:37:21 +08:00

125 lines
3.7 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
// application/common/library/Snowflake.php
namespace app\common\library;
class Snowflake
{
// 起始时间戳2023-01-01
const EPOCH = 1672502400000;
// 机器ID位数
const WORKER_ID_BITS = 5;
const DATACENTER_ID_BITS = 5;
const SEQUENCE_BITS = 12;
// 最大值
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);
// 移位
const WORKER_ID_SHIFT = self::SEQUENCE_BITS;
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;
protected $workerId;
protected $datacenterId;
protected $sequence = 0;
protected $lastTimestamp = -1;
public function __construct($workerId = 1, $datacenterId = 1)
{
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");
}
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");
}
$this->workerId = $workerId;
$this->datacenterId = $datacenterId;
}
public function nextId()
{
$timestamp = $this->timeGen();
if ($timestamp < $this->lastTimestamp) {
$diff = $this->lastTimestamp - $timestamp;
throw new \Exception("Clock moved backwards. Refusing to generate id for {$diff} milliseconds");
}
if ($this->lastTimestamp == $timestamp) {
$this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE;
if ($this->sequence == 0) {
$timestamp = $this->tilNextMillis($this->lastTimestamp);
}
} else {
$this->sequence = 0;
}
$this->lastTimestamp = $timestamp;
return (($timestamp - self::EPOCH) << self::TIMESTAMP_LEFT_SHIFT) |
($this->datacenterId << self::DATACENTER_ID_SHIFT) |
($this->workerId << self::WORKER_ID_SHIFT) |
$this->sequence;
}
protected function tilNextMillis($lastTimestamp)
{
$timestamp = $this->timeGen();
while ($timestamp <= $lastTimestamp) {
$timestamp = $this->timeGen();
}
return $timestamp;
}
protected function timeGen()
{
return floor(microtime(true) * 1000);
}
/**
* 解析雪花ID
* @param int $id
* @return array
*/
public static function parseId($id)
{
$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
];
}
/**
* 生成ID单例模式
* @return int
*/
public static function generate()
{
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();
}
}