thinkphp做短链接必须用自增id转62进制生成短码,禁用md5/uniqid;建表设auto_increment起始值1000;路由只取path_info中的短码并校验格式;跳转强制header+exit且优先查redis缓存。

ThinkPHP 做短链接不难,但直接套用 url() 或随手 md5() 截取会踩坑——生成不可逆、高碰撞、跳转被劫持、缓存失效、并发重复入库都是高频问题。
短码必须用自增 ID 转 62 进制,别碰 md5/uniqid
用 md5($url) 取前 6 位看似快,但两个不同长链可能生成相同短码(碰撞),且无法反查原始 URL;uniqid() 在高并发下大概率重复。真正可靠的是数据库自增 id + 自定义 62 进制编码。
- 建表时确保
id是AUTO_INCREMENT,起始值设为 1000 避免短码过短(如id=1编码后是"1") - 别手写除法循环,封装一个
idToShort($id)函数,字符集用'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' - 插入前用
INSERT INTO short_links (long_url, short_code) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id)防并发重复
路由必须匹配 PATH_INFO,拒绝 query 参数参与跳转
用户访问 /s/abc123 时,短码只能从路径段提取;若允许 /s/abc123?to=https://evil.com 并直接跳转,就是开放重定向漏洞。
- 路由定义写死路径变量:
Route::get('/s/<hash>', 'Index/Short@redirect')->pattern(['hash' => '\w{4,8}']);</hash> - 控制器里只取
$this->request->param('hash'),**不读 $_GET、不拼接任何外部输入到跳转地址** - 校验短码格式:仅限字母数字、长度 4–8 位;查库失败或
long_url不以http开头(且非内网白名单域名),直接throw new HttpException(404)
跳转必须 header+exit,缓存要分层设计
漏掉 exit 会导致后续代码执行,可能泄露日志、触发重复统计甚至 XSS;全靠数据库查会拖慢高并发场景。
- 跳转逻辑强制三步:
header('Location: ' . $url);→http_response_code(302);→exit;(注意:header('Location:...')本身已发 302,第二步可省,但不能只写第二步) - 缓存优先级:先查 Redis(key 为
short_abc123),命中则直跳;未命中再查 DB,并写回 Redis(TTL 建议 3600 秒) - 模板里别用
{:url()}生成短链——它在编译期固化 URL,缓存后永远不变;短链生成必须走控制器逻辑
Url::build() 不能直接用于短链生成,要重写 buildHandler
url() 是路由解析器,不是字符串拼接工具。传数组如 ['index', 'short', 'redirect'] 会生成带完整路径的长 URL,和短链目标冲突。必须接管构建逻辑。
- 在全局服务注册处(如
app/common.php)调用Url::setBuildHandler() - 判断
$url[0]是否以'short/'开头,是则走哈希映射:查short_map表,无则生成 6 位随机hash_code并入库 - 注意:
$url是数组时,第一个元素是路由名或控制器/操作,不能直接传原始长链字符串
最易忽略的是并发去重和跳转出口控制——INSERT ON DUPLICATE KEY UPDATE 和 header+exit 不是可选项,是保命线。算法再花哨,这两点没卡死,上线就出事。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











