thinkphp 的 think-captcha 仅支持图形验证码,不支持语音生成,因其底层仅依赖 gd 库绘图,无 tts 或音频处理逻辑;需自行实现语音接口,通过调用云 tts、本地 espeak 或数字音频拼接生成 mp3,并将验证码字符串存入 session 供后端验证。

ThinkPHP 本身不提供语音验证码能力,必须靠外部 TTS 服务或本地命令行工具生成音频文件,再由 PHP 输出路径或流。直接调用 Captcha 类只能出图,不能出声。
为什么不能用 think-captcha 直接生成语音
think-captcha 是纯图形验证码组件,底层依赖 GD 库绘图,所有方法(如 entry()、create())只返回图像响应(Content-Type: image/png)。它没有音频生成逻辑,也不处理语音合成、文件拼接或 TTS 请求封装。
- 试图给
Captcha传type => 'mp3'或类似参数——会静默忽略,仍输出图片 - 在控制器里调用
captcha_check()验证语音输入?该函数只比对 Session 中的图形码明文,和语音内容无关 - 混淆点:有些文档把“语音验证码”和“图形验证码+语音播报按钮”混为一谈,后者是前端 JS 播放预录提示音,不是真 TTS
推荐做法:PHP 后端生成语音文件 URL,前端播放
核心思路是绕过 think-captcha,自己写一个语音生成接口,返回可直接被 <audio></audio> 加载的 MP3/WAV 路径。有三种落地方式:
-
调用百度/腾讯云 TTS API:需注册平台、获取
access_token,POST 到https://tsn.baidu.com/text2audio,注意tex参数要urlencode(),且必须带vol、spd等参数,否则返回错误码 -
本地执行 espeak:Ubuntu 上
sudo apt install espeak,PHP 中用exec("espeak -v zh -w /tmp/verify_".uniqid().".mp3 '您的验证码是 3729'"),但要注意www-data用户是否有权限写入/tmp和执行二进制 -
数字片段拼接(最稳):提前录好
0.mp3~9.mp3和prompt.mp3,PHP 用file_get_contents()读取并合并(可用ffmpeg命令或php-ffmpeg库),生成完整语音文件
Session 存什么、前端怎么传、后端怎么验
图形码靠 Session 存原始字符串,语音码也得存——但不能存语音文件内容,而要存**本次生成的验证码数字本身**(比如 3729),和图形码完全一样。区别只在呈现方式。
- 生成语音接口时,先用
session('voice_code', $code)把数字写入 Session,再调 TTS 或拼接音频 - 前端提交表单时,用户听到语音后手动输入,把文本值(如
input[name="voice_code"])发到后端 - 验证逻辑就是:
if (session('voice_code') === input('post.voice_code')) { /* 通过 */ },不需要任何额外扩展 - 务必在验证后立即
session('voice_code', null)清除,避免重放
容易被忽略的兼容性与安全细节
语音验证码不是“加个 audio 标签”就完事,几个硬伤常被跳过:
- MP3 文件路径必须能被 Web 服务器直接访问(比如放在
public/voice/下),不能藏在app/里;否则前端<audio src="/voice/xxx.mp3"></audio>会 404 - 如果用
readfile()输出音频流,记得设 header:header('Content-Type: audio/mpeg');,否则浏览器无法识别 - 验证码数字若含前导零(如
0283),PHP 默认转成整数会丢掉,必须全程当字符串处理:(string)rand(1000, 9999)或用sprintf('%04d', $n) - 别让同一个语音 URL 被反复请求——每次生成都要用
uniqid()或时间戳命名文件,否则 CDN 或浏览器缓存会导致用户听到旧码
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











