* SnowFlake.php

<?php


namespace CarV2\Utils;


class SnowFlake
{
const START_STAMP = 1640966400000; // "2022-01-01"
const SEQUENCE_BIT = 12; // 序列号占用的位数
const MACHINE_BIT = 5; // 机器标识占用的位数
const DATACENTER_BIT = 5; // 数据中心占用的位数

const MAX_DATACENTER_NUM = -1 ^ (-1 << self::DATACENTER_BIT); // 0x8000000000000001
const MAX_MACHINE_NUM = -1 ^ (-1 << self::MACHINE_BIT);
const MAX_SEQUENCE = -1 ^ (-1 << self::SEQUENCE_BIT);

const MACHINE_LEFT = self::SEQUENCE_BIT;
const DATACENTER_LEFT = self::SEQUENCE_BIT + self::MACHINE_BIT;
const TIMESTAMP_LEFT = self::DATACENTER_LEFT + self::DATACENTER_BIT;

/** @var int */
private $datacenterId;
/** @var int */
private $machineId;
/** @var int */
private $sequence = 0;
/** @var int */
private $lastStamp = -1;

public function __construct(int $dcId, int $mId) {
if ($dcId < 0 || self::MAX_DATACENTER_NUM < $dcId) {
throw new \InvalidArgumentException(
"datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
}
if ($mId < 0 || self::MAX_MACHINE_NUM < $mId) {
throw new \InvalidArgumentException(
"machineId can't be greater than MAX_MACHINE_NUM or less than 0");
}
$this->datacenterId = $dcId;
$this->machineId = $mId;
}

public function nextId() {
$currStamp = self::timeGen();
if ($currStamp < $this->lastStamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
if ($currStamp - $this->lastStamp == 0) {
$this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE;
if ($this->sequence == 0) {
$currStamp = $this->getNextMill();
}
} else {
$this->sequence = 0;
}
$this->lastStamp = $currStamp;
return ($currStamp - self::START_STAMP) << self::TIMESTAMP_LEFT //时间戳部分
| $this->datacenterId << self::DATACENTER_LEFT //数据中心部分
| $this->machineId << self::MACHINE_LEFT //机器标识部分
| $this->sequence; //序列号部分
}

private function getNextMill() {
$mill = self::timeGen();
while ($mill - $this->lastStamp <= 0) {
$mill = self::timeGen();
}
return $mill;
}

private static function timeGen() {
list($ms, $sec) = explode(' ', microtime());
return round((floatval($ms) + floatval($sec)) * 1000);
}
}

Usage:

public function genAppNo() : string {
$datacenterId = rand(1, 4);
$machineId = rand(1,31);
$sf = new SnowFlake($datacenterId, $machineId);
$nextId = strval($sf->nextId());
return date("ymd").substr($nextId, 6);
}

string(17) "22031195022706688"