php版 雪花ID转换12位base36工具
为了获取 更短&高可辨识度的 唯一字符串ID, 设计了一个 雪花ID转换12位base36工具 ,也可以将 base36 转换回 雪花ID
雪花算法格式图解:

<?php
namespace App\Utils;
use Exception;
/**
* 雪花ID转换工具
* 雪花ID为12个字符的base36字符串
*/
class SnowBase36
{
private static $charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
// 字符集长度
private static $base = 36;
//选填0,1 如果填0转换出的base36字符串首位可能为0打头,所以这里固定为1
private static $first_bin = '1';
// 数据中心ID配置 (5位) 用于转换回雪花ID时使用 ,根据实际配置修改
private static $datacenter_id_bin = '00000';
/**
* 雪花ID 转 12个字符的base36字符串
* @param string $snowflakeId
* @return string
*/
public static function id2base36($snowflakeId): string
{
$snowflakeIdBin = self::decimal2binary($snowflakeId);
$binary = str_pad($snowflakeIdBin, 64, '0', STR_PAD_LEFT);
$timestamp = substr($binary, 1, 41); // 42位时间截(毫秒级)
$worker_id = substr($binary, 42, 5); // 5位workerId
// $datacenter_id = substr($binary, 47, 5); // 5位datacenterId // 默认 "00000"
$sequence_id = substr($binary, 52, 12); // 12位序列号
// var_dump(self::$first_bin . $timestamp . $worker_id . $sequence_id);
return self::base36encode(self::$first_bin . $timestamp . $worker_id . $sequence_id);
}
/**
* 12个字符的base36字符串 转换回 雪花ID
* @param string $base36Str
* @return string
*/
public static function base362id($base36Str): string
{
$binary = self::base36decode($base36Str); // 59位二进制字符串
$timestamp = substr($binary, 1, 41); // 42位时间截(毫秒级)
$worker_id = substr($binary, 42, 5); // 5位workerId
$datacenter_id = self::$datacenter_id_bin; // 默认 "00000"
$sequence_id = substr($binary, 47, 12); // 12位序列号
// 转换回二进制时,雪花算法第一位是固定值是 0
$resBin = '0' . $timestamp . $worker_id . $datacenter_id . $sequence_id;
return self::binary2decimal($resBin);
}
/**
* 10进制字符串转二进制字符串
* @param string $decimal
* @throws \Exception
* @return string
*/
public static function decimal2binary($decimal)
{
// 使用BCMath处理大数
if (! function_exists('bcmul')) {
throw new Exception("需启用BCMath扩展");
}
$binary = '';
$num = $decimal;
while (bccomp($num, '0') > 0) {
$remainder = bcmod($num, '2');
$binary = $remainder . $binary;
$num = bcdiv($num, '2', 0);
}
return $binary;
}
/**
* 二进制转10进制(字符串形式)
* @param string $binary
* @return string
*/
public static function binary2decimal($binary): string
{
$decimal = '0';
for ($i = 0; $i < strlen($binary); $i++) {
$decimal = bcmul($decimal, '2', 0); // 十进制数 × 2
if ($binary[$i] === '1') {
$decimal = bcadd($decimal, '1', 0); // 加当前位值
}
}
return $decimal;
}
/**
* 十进制转36进制(12位)
* @param mixed $decimal
* @return int|string
*/
private static function decimal2base36($decimal): string
{
if ($decimal === '0') {
return '0';
}
$result = '';
while (bccomp($decimal, '0', 0) > 0) {
$remainder = bcmod($decimal, self::$base);
$result = self::$charset[$remainder] . $result;
$decimal = bcdiv($decimal, self::$base, 0);
}
// 确保12位长度(高位补零)
return str_pad($result, 12, self::$charset[0], STR_PAD_LEFT);
}
/**
* 二进制转36进制(12位)
* @param string $binary 59位二进制字符串
* @return string 12位大写字符串
* @throws Exception
*/
public static function base36encode($binary): string
{
// 验证输入格式
if (strlen($binary) !== 59 || ! preg_match('/^[01]+$/', $binary)) {
throw new Exception("无效的二进制输入:必须为59位0/1字符串");
}
// 二进制转十进制大数(字符串形式)
$decimal = self::binary2decimal($binary);
// 十进制大数转36进制
return self::decimal2base36($decimal);
}
/**
* 36进制转二进制(59位)
* @param mixed $base36
* @throws \Exception
* @return string
*/
public static function base36decode($base36): string
{
// 验证输入格式
if (strlen($base36) !== 12 || ! preg_match('/^[0-9A-Za-z]+$/', $base36)) {
throw new Exception("无效的Base36输入:必须为12位0-9/A-Z字符");
}
// 字符到数值的映射表
$reverseCharset = array_flip(str_split(self::$charset));
// 将Base36转为十进制大数
$decimal = '0';
for ($i = 0; $i < 12; $i++) {
$char = strtoupper($base36[$i]);
if (! isset($reverseCharset[$char])) {
throw new Exception("非法字符 '$char' 在Base36字符串中");
}
$digit = $reverseCharset[$char];
// 大数运算:decimal = decimal * base + digit
$decimal = bcmul($decimal, self::$base, 0);
$decimal = bcadd($decimal, $digit, 0);
}
// 十进制大数转二进制字符串
$binary = self::decimal2binary($decimal);
// 补齐59位(高位补零)
return str_pad($binary, 59, '0', STR_PAD_LEFT);
}
}

浙公网安备 33010602011771号