erikwang2013 / hashids
Hashids integration for Laravel, Webman, ThinkPHP, Hyperf, Yii 2, and Yii 3 (multi-connection, API aligned with vinkla/hashids).
Requires
- php: ^8.0
- hashids/hashids: ^4.1 || ^5.0
Requires (Dev)
- illuminate/support: ^9.0|^10.0|^11.0|^12.0
- mockery/mockery: ^1.6
- phpunit/phpunit: ^9.6|^10.5|^11.0
Suggests
- ext-bcmath: hashids/hashids 需要 bcmath 或 gmp 之一,否则首次编码抛 RuntimeException。
- ext-gmp: hashids/hashids 需要 bcmath 或 gmp 之一(两者二选一即可)。
- hyperf/framework: For Hyperf integration.
- laravel/framework: For Laravel integration.
- topthink/framework: For ThinkPHP integration.
- workerman/webman: For Webman integration.
- yiisoft/di: For Yii 3 integration (Yiisoft\Di\Container).
- yiisoft/yii2: For Yii 2 integration.
Provides
None
Conflicts
None
Replaces
None
README
Languages: 中文 · English · 한국어 · Русский · Deutsch · Français · Español · Português · हिन्दी · العربية · বাংলা · Bahasa Indonesia · 日本語
哈希迪 Hashy — 项目宠物,胸口的 # 是它的招牌
把数据库自增 ID 换成短小、不可猜测的字符串,一套 API 同时跑在 Laravel、Webman、ThinkPHP、Hyperf、Yii 2、Yii 3 上。
底层依赖 hashids/hashids v5;配置与用法对齐 vinkla/hashids(多连接、默认连接、HashidsManager + 工厂),可平滑替换。
项目说明
Hashids 是一款短 ID 生成器,可将数字 ID(如数据库主键)编码为短小、唯一且不可猜测的字符串。它不同于 UUID 或雪花 ID——Hashids 更适合用于面向用户的场景(URL、分享码、订单号等),在保持短小可读的同时隐藏原始数字。
本包 erikwang2013/hashids 是 Hashids 的 PHP 多框架集成层,在设计上参考并对齐了 vinkla/hashids 的 API 风格(多连接、默认连接、Manager + Factory 模式),并扩展支持了国内常用的其他 PHP 框架。
核心特性:
- 多框架兼容:同一套 API 同时支持 Laravel、Webman、ThinkPHP、Hyperf、Yii 2、Yii 3,迁移成本极低。
- 多连接支持:一个应用可同时配置多套 Salt/Length 组合(如用户 ID 与订单 ID 使用不同盐值),通过
connection('xxx')切换。 - 无框架依赖:不依赖任何特定框架即可独立使用,直接
new HashidsManager($config, $factory)即可工作。 - 对齐 vinkla/hashids:Laravel 下 Facade、容器绑定、
config/hashids.php格式均与 vinkla/hashids 一致,可平滑替换。 - 框架原生风格:各框架集成遵循各自的惯用法——Laravel 用 ServiceProvider + Facade,Webman 用 Plugin + Bootstrap,ThinkPHP 用 Service,Hyperf 用 ConfigProvider,Yii 2 用 Bootstrap + 应用组件,Yii 3 用 ServiceProvider。
适用场景:
| 场景 | 说明 |
|---|---|
| 隐藏数据库自增 ID | 将 user_id=100 映射为 /user/3kTMd,避免暴露业务规模 |
| 生成短链接/分享码 | 比 UUID 更短,比随机字符串可控 |
| 订单号/流水号 | 可读性好,便于客服沟通与日志排查 |
| 多租户/多模块隔离 | 不同连接使用不同 Salt,确保编码空间相互独立 |
注意事项:
- Hashids 是 编码(encode/decode)而非加密。Salt 仅增加猜测难度,不可用于安全敏感场景(如 token、密码)。
- 一旦上线后修改 Salt 或 Length,所有已编码的 ID 将变为无效,请提前规划并固定配置。
- Salt 留空等于没有保护:空 salt 下编码结果可枚举(
encode(1)、encode(2)… 顺序可预测)。写成env('HASHIDS_SALT', '')时,漏配环境变量既不报错也不告警,只是静默降级成空 salt —— 上线前请确认盐已设置。 - 常驻内存框架(Webman / Hyperf)下改配置需要重启进程:
HashidsManager在构造时快照配置并永久缓存连接, 改config/hashids.php(或用配置中心换盐)不会热生效,reload或重启后才会。
项目结构
hashids/
├── src/
│ ├── HashidsManager.php # 核心:多连接解析、实例缓存、默认连接代理
│ ├── HashidsFactory.php # 核心:由连接配置构建 Hashids 实例
│ ├── Mascot.php # 项目宠物「哈希迪 Hashy」的 ASCII 版
│ ├── Install.php # Webman 安装 / 卸载钩子
│ ├── Laravel/
│ │ ├── HashidsServiceProvider.php # 容器单例 + 别名 + 配置发布
│ │ └── Facades/Hashids.php # Facade(默认连接)
│ ├── Webman/
│ │ └── Bootstrap.php # 进程启动时注册容器定义
│ ├── ThinkPHP/
│ │ └── HashidsService.php # think\Service 注册绑定
│ ├── Hyperf/
│ │ ├── ConfigProvider.php # 依赖映射 + 配置发布
│ │ ├── HashidsManagerFactory.php
│ │ └── HashidsClientFactory.php
│ ├── Yii2/
│ │ ├── Bootstrap.php # BootstrapInterface:注册 Yii::$container
│ │ └── Component.php # 应用组件:Yii::$app->hashids
│ ├── Yii3/
│ │ └── ServiceProvider.php # ServiceProviderInterface:返回六个定义
│ └── config/plugin/erikwang2013/hashids/ # Webman 插件骨架(安装时拷贝到项目)
│ ├── app.php # enable 开关
│ └── bootstrap.php # 注册 Webman\Bootstrap
├── config/
│ ├── hashids.php # 扁平配置:Laravel / Webman / ThinkPHP
│ └── autoload/hashids.php # Hyperf 配置(结构同扁平,路径不同)
├── tests/
│ ├── HashidsManagerTest.php # 核心行为
│ ├── HashidsFactoryTest.php
│ ├── InstallTest.php
│ ├── MascotTest.php # 项目宠物:字形对齐与问候
│ ├── Laravel/ · Webman/ · ThinkPHP/ · Hyperf/ · Yii2/ · Yii3/ # 各框架适配测试
│ ├── Config/PluginConfigTest.php
│ ├── Contract/ # 真框架契约测试(CI 单独作业,见下)
│ └── Support/FrameworkStubs.php # 测试用的框架类替身
├── docs/
│ ├── mascot.svg # 项目宠物「哈希迪 Hashy」
│ ├── architecture.svg # 架构设计图
│ ├── features.svg # 功能设计图
│ └── lifecycle.svg # 生命周期图
├── .github/workflows/release.yml # 推送 main 后自动打 tag 并发 release
├── composer.json
└── phpunit.xml.dist
vendor/、composer.lock等依赖产物未列出;src/config/是插件骨架,config/是可发布的配置样例,两者用途不同。
架构设计
四层单向依赖,上层依赖下层,反向不成立:
| 层 | 职责 | 位置 |
|---|---|---|
| 应用调用层 | Facade、容器 / 助手函数、构造注入三种入口 | 业务代码 |
| 框架适配层 | 只做接线:容器绑定、配置读取、配置发布 | src/<Framework>/ |
| 核心层 | 多连接管理与实例构建,不依赖任何框架 | src/HashidsManager.php、src/HashidsFactory.php |
| 底层依赖 | 实际编解码实现 | hashids/hashids |
核心层是整个包的重心:HashidsManager 持有配置,connection($name) 按需构建并缓存连接,__call() 把未指定连接的方法调用转发到默认连接;HashidsFactory::make() 是唯一构造 Hashids\Hashids 的地方。六个框架的适配类加起来约 490 行(含 Facade、两个 Hyperf 工厂与 Yii 2 的应用组件)——它们只负责把内核接进各自的容器。
功能设计
六个能力分组,全部围绕同一个内核:
- 编解码 API:
encode()/decode()/encodeHex()/decodeHex(),经__call()落到默认连接,无需显式connection()。 - 多连接管理:
connection('alternative')切换;懒加载构建,同一连接只构建一次。 - 多框架适配:Laravel 用
ServiceProvider、Webman 用Install+Bootstrap、ThinkPHP 用Service、Hyperf 用ConfigProvider、Yii 2 用BootstrapInterface+ 应用组件、Yii 3 用ServiceProviderInterface,各自遵循框架惯用法。 - 容器绑定:类名(
HashidsManager、HashidsFactory、Hashids\Hashids)与字符串键('hashids'、'hashids.factory'、'hashids.connection')双轨绑定,六框架一致;后两个字符串键与 vinkla/hashids 同名,便于迁移。 - 配置与发布:六个框架的配置形状一致,都是扁平数组(根级
default+connections),差别只在放哪儿——Laravel / Webman / ThinkPHP 是config/hashids.php,Hyperf 是config/autoload/hashids.php,Yii 2 是config/params.php里的params['hashids'],Yii 3 直接传给ServiceProvider构造函数(框架不约定配置文件)。 - 零框架依赖内核:
new HashidsManager($config, $factory)即可工作,$config入参容忍非数组(归一化为空数组)。
生命周期
| 阶段 | 发生了什么 |
|---|---|
| 安装期 | composer require 拉包 → Laravel 自动发现 / Webman 安装钩子拷贝插件骨架 / Yii 2 在应用配置里登记 Bootstrap 或组件 / Yii 3 在 ContainerConfig::withProviders() 里登记 ServiceProvider → 加载 salt / length / alphabet |
| 运行期 | 容器解析出 HashidsManager → encode() / decode() → __call() 转发到 connection($name) → 命中缓存直接复用,未命中才 HashidsFactory::make() 构建并写入 $connections → 返回短 ID 或原数字 |
| 卸载期 | composer remove 触发 Install::uninstall(),移除 config/plugin/erikwang2013/hashids;config/hashids.php 会保留,是否清理由使用者决定 |
连接是按需构建的:只调用默认连接的进程,不会为 alternative 之类未使用的连接付出任何构建成本。
安装
composer require erikwang2013/hashids
运行环境:底层
hashids/hashids必须有ext-bcmath或ext-gmp(二选一),否则第一次编码就会抛RuntimeException: Missing math extension for Hashids。这两个扩展在hashids/hashids里只列于suggest, 而 Composer 2 已不再打印 suggest —— 于是在精简镜像(如php:8.3-fpm-alpine)上安装期毫无提示、运行期才炸, 且每个用到 Hashids 的请求都会 500。本包composer.json的suggest已补上这两个键,但仍需你确认扩展已启用。
配置结构(Laravel / Webman / ThinkPHP / Yii 2 / Yii 3)
与仓库 config/hashids.php 一致:
default:默认连接名(如main)。connections:连接名 =>salt、length、可选alphabet。
Hyperf 的配置文件路径不同(
config/autoload/hashids.php),但结构相同,见下文 Hyperf 小节。Yii 2 不用单独的配置文件,直接把同一个数组放进应用
config/params.php的params['hashids'],见下文 Yii 2 小节。Yii 3 也不读配置文件,把同一个数组传给
ServiceProvider构造函数即可,见下文 Yii 3 小节。
无框架用法
可直接实例化管理器:
use Erikwang2013\Hashids\HashidsFactory; use Erikwang2013\Hashids\HashidsManager; $manager = new HashidsManager( require __DIR__ . '/config/hashids.php', new HashidsFactory() ); $hash = $manager->encode(1, 2, 3); $ids = $manager->decode($hash);
Laravel
与 vinkla/hashids 类似:容器注册 HashidsManager,多连接;默认连接支持 Facade 与方法转发。
Laravel 5.5+ 会读取本包 composer.json 的 extra.laravel,自动注册 HashidsServiceProvider 与 Facade 别名 Hashids。
发布配置(可选)
php artisan vendor:publish --tag=hashids-config
生成 config/hashids.php。若不发布,扩展包会在注册阶段合并内置默认配置。
Facade(默认连接)
use Erikwang2013\Hashids\Laravel\Facades\Hashids; $hash = Hashids::encode(1, 2, 3); $numbers = Hashids::decode($hash);
指定连接
use Erikwang2013\Hashids\Laravel\Facades\Hashids; $hash = Hashids::connection('alternative')->encode(100);
依赖注入 HashidsManager
use Erikwang2013\Hashids\HashidsManager; public function __construct(private HashidsManager $hashids) {} $this->hashids->encode(1); $this->hashids->connection('alternative')->encode(2);
注入底层 Hashids\Hashids(默认连接)
use Hashids\Hashids; public function __construct(private Hashids $hashids) {}
运行 Laravel 集成需要项目已安装 laravel/framework(含 illuminate/support 等)。本包将 illuminate/* 列为 require-dev,仅供包自身测试。
Webman
通过 Install 在安装时拷贝 config/plugin/erikwang2013/hashids 与根目录 config/hashids.php,并由插件 bootstrap 向 Webman 容器注册 HashidsManager。
项目的 composer.json 中若已有 support\Plugin::install / update / uninstall 钩子,安装本包时会自动执行安装脚本(WEBMAN_PLUGIN = true)。
安装后文件
config/plugin/erikwang2013/hashids/app.php:enable开关。config/plugin/erikwang2013/hashids/bootstrap.php:注册Erikwang2013\Hashids\Webman\Bootstrap。config/hashids.php:多连接配置(首次安装或确认覆盖时写入)。
若自动拷贝未执行,可从扩展包内手动复制上述路径的示例配置。
关闭插件(config/plugin/erikwang2013/hashids/app.php)
<?php return [ 'enable' => false, ];
容器绑定
Erikwang2013\Hashids\HashidsManager/'hashids'Erikwang2013\Hashids\HashidsFactory/'hashids.factory'Hashids\Hashids/'hashids.connection'(默认连接实例)
控制器示例
use support\Request; use Erikwang2013\Hashids\HashidsManager; use Hashids\Hashids; class DemoController { public function index(Request $request, HashidsManager $manager) { $hash = $manager->encode(1, 2, 3); $client = \support\Container::instance()->get(Hashids::class); $hash2 = $client->encode(4); return json(['hash' => $hash, 'hash2' => $hash2]); } }
指定连接
$manager->connection('alternative')->encode(99);
Composer 卸载包时会触发 Plugin::uninstall,移除 config/plugin/erikwang2013/hashids;不会删除 config/hashids.php,是否保留由你决定。
ThinkPHP
通过 自定义服务类 注册 HashidsManager;配置仍为顶层含 default 与 connections 的 config/hashids.php。
注册服务
在应用 config/service.php(具体路径随 TP 版本可能不同)的 services 中加入:
<?php return [ // ... \Erikwang2013\Hashids\ThinkPHP\HashidsService::class, ];
若使用应用级 app/AppService.php,也可在 register() 中写入等价绑定。
配置文件
将扩展包内 config/hashids.php 复制到应用 config/hashids.php(或自行合并同名配置)。
<?php return [ 'default' => 'main', 'connections' => [ 'main' => [ 'salt' => env('HASHIDS_SALT', ''), 'length' => (int) env('HASHIDS_LENGTH', 0), ], ], ];
使用示例
use Erikwang2013\Hashids\HashidsManager; use think\facade\App; $manager = App::make(HashidsManager::class); $hash = $manager->encode(10, 20);
$manager = app('hashids');
use Hashids\Hashids; $client = app(Hashids::class); $hash = $client->encode(1);
app(HashidsManager::class)->connection('alternative')->encode(100);
ThinkPHP 集成继承 think\Service,需在 topthink/framework 环境中使用(本包列为 suggest)。
Hyperf
Composer extra.hyperf.config 会载入 ConfigProvider,向容器注册 HashidsFactory、HashidsManager、默认连接的 Hashids\Hashids。
将扩展包内 config/autoload/hashids.php 复制到项目 config/autoload/hashids.php(或使用项目的配置发布命令)。
该文件必须返回扁平数组(根级 default + connections)。
Hyperf 的 ConfigFactory 按文件名归并(Arr::set($config, 'hashids', require $file)),所以 config('hashids') 返回的就是文件内容本身——不要再套一层 'hashids' =>。套了的话 connections 不可达,取连接时会抛 Hashids connection [main] is not configured。
<?php declare(strict_types=1); return [ 'default' => 'main', 'connections' => [ 'main' => [ 'salt' => env('HASHIDS_SALT', ''), 'length' => (int) env('HASHIDS_LENGTH', 0), ], ], ];
容器绑定
| 抽象 | 实现 |
|---|---|
Erikwang2013\Hashids\HashidsFactory / 'hashids.factory' |
默认构造 |
Erikwang2013\Hashids\HashidsManager / 'hashids' |
HashidsManagerFactory |
Hashids\Hashids / 'hashids.connection' |
HashidsClientFactory(默认连接) |
Controller / 构造函数注入
<?php declare(strict_types=1); namespace App\Controller; use Erikwang2013\Hashids\HashidsManager; use Hashids\Hashids; class DemoController { public function index(HashidsManager $manager, Hashids $hashids) { $h1 = $manager->encode(1, 2, 3); $h2 = $hashids->encode(4); $alt = $manager->connection('alternative')->encode(99); return compact('h1', 'h2', 'alt'); } }
$manager = \Hyperf\Context\ApplicationContext::getContainer()->get( \Erikwang2013\Hashids\HashidsManager::class );
六个框架的配置结构一致(根级 default + connections),差别只在放哪儿:Hyperf 用 config/autoload/hashids.php,Laravel / Webman / ThinkPHP 用 config/hashids.php,Yii 2 放进 config/params.php 的 params['hashids'],Yii 3 直接传给 ServiceProvider。
Yii 2
适配器实现 yii\base\BootstrapInterface,把六个标识注册进 Yii::$container;另提供 yii\base\Component 子类,让 Yii 惯用的 Yii::$app->hashids 可用。
两种入口,按需选(不能互相替代)
入口 A:容器 —— config/web.php 的 bootstrap 数组
'bootstrap' => [ 'log', \Erikwang2013\Hashids\Yii2\Bootstrap::class, ],
use Erikwang2013\Hashids\HashidsManager; $manager = Yii::$container->get(HashidsManager::class); $hash = $manager->encode(10, 20); $alt = Yii::$container->get('hashids')->connection('alternative')->encode(100);
入口 B:应用组件 —— config/web.php 的 components 数组
'components' => [ 'hashids' => [ 'class' => \Erikwang2013\Hashids\Yii2\Component::class, 'config' => [ 'default' => 'main', 'connections' => [ 'main' => ['salt' => getenv('HASHIDS_SALT'), 'length' => 6], ], ], ], ],
Yii::$app->hashids->encode(1); // __call 转默认连接 Yii::$app->hashids->connection('alternative')->encode(100); // 切连接 Yii::$app->hashids->manager; // 取底层 HashidsManager
为什么两种入口都要有:
yii\di\ServiceLocator::get()不会回落到Yii::$container, 只认登记在自己_definitions里的组件。所以只登记bootstrap并不会让Yii::$app->hashids生效;反过来只配components也不会让Yii::$container->get()拿到实例。要用哪个就配哪个。
配置文件
Yii 2 没有 config/hashids.php 这种约定,配置放在应用 config/params.php 的 params['hashids']:
return [ // ... 'hashids' => [ 'default' => 'main', 'connections' => [ 'main' => [ // Yii 2 没有 Laravel 那样的全局 env() 助手,用 getenv() // 或你自己应用里的 env 机制。 'salt' => getenv('HASHIDS_SALT') ?: '', 'length' => (int) (getenv('HASHIDS_LENGTH') ?: 0), ], ], ], ];
Bootstrap 读的就是 Yii::$app->params['hashids'];该键缺失时按空数组处理(默认连接名回落
main),不报错 —— 但缺 salt 时哈希可枚举,上线前请确认盐已设置。
默认连接是惰性解析的:
bootstrap阶段只登记定义,真正取Hashids\Hashids时才构建连接。 配置写错不会让整个应用启动失败,而是在取用时抛InvalidArgumentException。
Yii 2 集成继承 yii\base\BootstrapInterface / yii\base\Component,需在 yiisoft/yii2
环境中使用(本包列为 suggest)。
Yii 3
Yii 3 是一组 PSR-11 容器(yiisoft/di)装配起来的包,没有 Yii 2 那种应用配置数组。
适配器是一个实现 Yiisoft\Di\ServiceProviderInterface 的类,getDefinitions() 返回六个容器标识。
注册
use Erikwang2013\Hashids\Yii3\ServiceProvider; use Yiisoft\Di\Container; use Yiisoft\Di\ContainerConfig; $config = ContainerConfig::create() ->withProviders([ // 配置直接传给 provider —— Yii 3 不约定配置文件位置, // 值从哪来(params、env、配置中心)由应用自己决定。 new ServiceProvider($params['hashids'] ?? []), ]); $container = new Container($config);
使用
use Erikwang2013\Hashids\HashidsFactory; use Erikwang2013\Hashids\HashidsManager; use Hashids\Hashids; $manager = $container->get(HashidsManager::class); $hash = $manager->encode(10, 20); // 默认连接 $container->get('hashids')->connection('alternative')->encode(100); // 字符串别名 + 切连接 $container->get(Hashids::class)->encode(1); // 按类名取默认连接 $container->get(HashidsFactory::class); // 工厂
容器自带单例语义(同一个 id 每次 get() 返回同一实例),所以别名与 FQCN 拿到的是同一套连接,
适配器的定义只要转发即可,不必预先造对象。
报错类名会多包一层:默认连接是惰性解析的,装配阶段不会失败;但取用时容器会把本包的
InvalidArgumentException包进Yiisoft\Di\BuildingException抛出 ——catch里写InvalidArgumentException是接不住的,要看$e->getPrevious()。
provider 的定义优先于普通 definitions:真容器的装配顺序是先
definitions、后providers, 所以想覆盖 hashids 的绑定,改definitions里的同名键无效,要用extensions或改 provider。
Yii 3 集成实现 Yiisoft\Di\ServiceProviderInterface,需在 yiisoft/di 环境中使用
(本包列为 suggest)。
项目宠物
项目宠物是 哈希迪 Hashy——一只头顶天线、胸口印着 # 的圆角方块小宠物,矢量图见 docs/mascot.svg。
终端里没有 SVG,所以代码里留了一份等价的 ASCII 版本(Erikwang2013\Hashids\Mascot):
use Erikwang2013\Hashids\Mascot; echo Mascot::greet();
●
│
╭─────────╮
│ ◉ ◉ │
│ ‿ │
│ # │
╰──┬───┬──╯
╵ ╵
哈希迪 Hashy · 把数据库自增 ID 换成短小、不可猜测的字符串
Webman 插件首次发布 config/hashids.php 时会顺带打印一次这次问候;配置已存在时(例如重复执行 composer update)不会重复打扰。
开源不易,欢迎支持 / Open Source is Not Easy, Your Support is Welcome
微信 WeChat Pay 支付宝 Alipay
License
MIT. See LICENSE.

