1. GD 验证码不显示,先别急着改代码
PHP 里用 GD 图形库封装一个VerifyImage类来生成验证码,是登录模块里非常常见的做法。核心流程无非就是:随机取字符串、imagecreatetruecolor建画布、imagettftext把字符写上去、加点噪点和干扰线、最后imagepng输出。听起来简单,但真正跑起来的时候,很多人会遇到一个很典型的现象:页面一片空白,图片位置只有一个裂图图标,或者浏览器控制台报Failed to load resource,再或者验证码出来了但全是方块乱码。
这类问题的根源,八成不在你的随机逻辑,而在imagettftext的字体路径和 TTF 参数上。imagettftext和imagestring最大的区别就是它依赖一个真实存在的.ttf字体文件,路径写错、字体没导入fonts目录、或者相对路径的基准目录和你以为的不一样,都会导致验证码画不出来。更麻烦的是,GD 在字体加载失败时往往不会给你一个特别明确的报错,而是直接输出一张空白图,让你误以为是imagepng的问题。
这篇就按排障视角来走:先讲清楚VerifyImage类里哪些地方最容易出问题,然后说怎么用 TaoToken 配好 Codex 的config.toml,让 Codex 帮你逐行对照VerifyImage.php和test.php去定位字体路径、GD 输出、点击切换这几块。TaoToken 在这里的角色很明确,它只负责给 Codex 提供 Key 和 Base URL,不生成验证码,验证码还是你自己的 PHP 代码在跑。适合正在写 PHP 登录模块、被 GD 验证码空白或乱码卡住的同学。
2. 用 TaoToken 给 Codex 配好 Key 和 Base URL
在让 Codex 帮你查VerifyImage之前,得先把它接上。打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 注册账号,进控制台创建一个 API Key。这个 Key 就是你后面填进 Codex 配置里的凭证。
创建 Key 的入口在控制台的 API Keys 页面,直接访问 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 就能到。拿到 Key 之后先复制存好,后面配置要用。
这里要强调一个容易踩的点:Codex 的 Base URL 填https://taotoken.net/api,不要在后面加/v1,也不要带任何 UTM 参数。很多人习惯性地补/v1,结果请求路径拼出来就错了,Codex 直接连不上。TaoToken 的接入地址就是https://taotoken.net/api这个干净的形式。
如果你还没想好怎么组织 Codex 的配置,可以先看看接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有 Base URL 和 Key 的填写说明。想先在网页里验证一下模型通不通,也可以直接用模型对话 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 试一句,确认 Key 是有效的。
3. Codex 的 config.toml 可复制配置
Codex 的配置一般放在用户目录下的.codex/config.toml。下面这份是可以直接抄的,把你的API_KEY换成刚才创建的那串就行。
# ~/.codex/config.toml model = "gpt-4o" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"配置里几个字段的作用:
| 字段 | 值 | 说明 |
|---|---|---|
model | gpt-4o | 按你实际要用的模型名填 |
base_url | https://taotoken.net/api | 不带/v1,不带 UTM |
env_key | TAOTOKEN_API_KEY | 从环境变量读 Key,避免明文写进文件 |
wire_api | chat | 走 chat 接口 |
Key 建议用环境变量传,别直接写死在config.toml里。Linux 或 macOS 下这样设置:
export TAOTOKEN_API_KEY="你的API_KEY"Windows PowerShell 下:
$env:TAOTOKEN_API_KEY="你的API_KEY"设完环境变量后,重新开一个终端让变量生效,再启动 Codex。这样配置和密钥就分开了,换 Key 的时候只改环境变量,不用动配置文件。
4. 让 Codex 对照 VerifyImage.php 逐项排查
配置通了之后,就可以把VerifyImage.php和test.php一起丢给 Codex,让它按imagettftext、imagepng、点击切换这三块来查。下面是我实际用下来比较有效的提问方式,你可以直接参考。
先让 Codex 定位字体路径问题。VerifyImage类里那行$font_files = array('./fonts/arial.ttf', './fonts/consola.ttf');用的是相对路径,相对的是当前执行脚本的工作目录,不是VerifyImage.php所在目录。test.php里require_once 'MyClasses/VerifyImage.php';引入类,但test.php自己可能被放在别的目录,或者被<img src="test.php">从另一个路径请求,工作目录就变了,./fonts/arial.ttf自然找不到。
可以这样问 Codex:
这是我的 VerifyImage.php 和 test.php。 imagettftext 用的字体路径是 './fonts/arial.ttf', 但验证码输出空白。请帮我分析相对路径的基准目录问题, 并给出用 __DIR__ 拼接绝对路径的改法。Codex 一般会建议把字体路径改成基于__DIR__的绝对路径,比如:
$font_files = array( __DIR__ . '/fonts/arial.ttf', __DIR__ . '/fonts/consola.ttf', );这样不管从哪个目录请求test.php,字体路径都是稳定的。改完再跑,空白问题大概率就没了。
接着让 Codex 检查imagettftext的参数。这个函数的签名是imagettftext($image, $size, $angle, $x, $y, $color, $fontfile, $text),参数顺序错一个就会出问题。原代码里$x = (imagefontwidth($size) + 5) * ($i + 1);这里有个隐患:imagefontwidth接收的是字体大小,但它返回的是内置字体的字符宽度,和 TTF 的$size不是一套体系,算出来的$x可能让字符跑到画布外面。可以让 Codex 帮你把坐标计算换成更稳的方式,比如按$width / $code_length均分。
再让 Codex 看imagepng和输出头。header('content-type: image/png');之前不能有任何输出,包括 BOM、空格、echo。如果VerifyImage.php文件开头有 UTF-8 BOM,或者require_once之前有空白行,头信息就发出去了,图片也会坏。可以让 Codex 检查文件是否有 BOM,以及imagepng前有没有多余输出。
最后是test.php的点击切换逻辑。原代码里:
<img class="verifyimg" src="test.php" name="test.php" alt="验证码加载失败" title="看不清则点击图片" onclick="this.src = this.name+'?'+'img='+Math.random();">这段逻辑本身没问题,靠Math.random()加时间戳参数绕过缓存。但如果test.php因为字体问题返回了空白图,点击切换也只是换了一张空白图,看起来像"点击没反应"。所以排查顺序应该是先修字体,再验证点击切换。可以让 Codex 帮你确认this.name和this.src的拼接结果,确保每次点击请求的 URL 都带上了新的随机参数。
5. 验证请求与成功结果
改完字体路径后,先别急着在浏览器里点,直接用命令行验证test.php的输出是不是一张合法 PNG。在项目目录下执行:
php test.php > verify.png file verify.png如果输出类似verify.png: PNG image data, 100 x 40, 8-bit/color RGB, non-interlaced,说明 GD 已经正常生成图片了。如果file命令报的是ASCII text或者HTML document,那说明test.php输出的不是图片,多半是 PHP 报错信息混进去了,这时候去看php test.php直接打印的内容,错误就藏在那里。
再确认一下字体文件确实存在且可读:
ls -l fonts/arial.ttf fonts/consola.ttf两个文件都在,权限是-rw-r--r--这种可读状态就没问题。如果字体文件缺失,去系统字体目录拷一份,或者从可信来源下载一个开源 TTF 放进fonts目录。
浏览器里验证的时候,打开test.php应该直接看到一张带随机字符、噪点和干扰线的验证码图。刷新几次,字符会变,说明随机逻辑正常。然后在 HTML 页面里放上那个<img>,点击图片,URL 后面会多出?img=0.xxxx这样的随机参数,图片内容也跟着变,点击切换就通了。
如果你在验证过程中想换个模型再问 Codex,或者想直接在网页里贴报错让它分析,可以用模型对话 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 快速试。长期在项目里用 Codex 做编码和排查的话,Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 会更合适,额度按编码场景走。
6. 本篇常见错排查
6.1 验证码空白,但页面没报错
最常见的就是字体路径问题。imagettftext找不到字体文件时,GD 不会抛异常,而是静默失败,imagepng照样输出一张空白图。把./fonts/arial.ttf换成__DIR__ . '/fonts/arial.ttf'基本能解决。另外确认fonts目录和VerifyImage.php在同一层,别放错位置。
6.2 报错 imagettftext(): Could not find/open font
这个报错比空白好,至少它明说了。原因还是路径,但有时候是字体文件本身损坏或者不是合法的 TTF。用file fonts/arial.ttf看一下,正常应该显示TrueType font data。如果显示别的,换一个字体文件。
6.3 验证码乱码或全是方块
字体文件能加载,但字符显示成方块,通常是字体不支持你要输出的字符集。原代码CreateRandomVerifyCode里$code_type == 2会生成大小写字母,$code_type == 3还会加数字,这些 ASCII 字符一般字体都支持。如果你改成了中文验证码,就得换一个带中文字形的 TTF,比如思源黑体,否则就是方块。
6.4 图片输出坏掉,浏览器显示裂图
检查header('content-type: image/png');之前有没有任何输出。VerifyImage.php文件开头的 BOM、require_once前的空行、test.php里的echo,都会破坏图片流。用编辑器把文件另存为"无 BOM 的 UTF-8"。
6.5 点击切换没反应
先确认test.php单独访问时能出图。如果单独访问是空白,点击切换当然也是空白。如果单独访问正常,检查onclick里this.name的值是不是test.php,以及拼接后的 URL 有没有被浏览器缓存。加Math.random()就是干这个的,确认它没被去掉。
6.6 Codex 连不上 TaoToken
回头检查config.toml里的base_url是不是https://taotoken.net/api,有没有手滑加了/v1。再确认环境变量TAOTOKEN_API_KEY在当前终端里能echo出来。如果 Key 无效,去 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 重新生成一个。接入细节以文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 为准。
7. 把 Codex 接进你的 PHP 排障流程
字体路径改完、test.php能稳定出图之后,你会发现VerifyImage这类 GD 问题其实就那么几个坑:路径、参数顺序、输出头、字符集。真正花时间的不是改代码,而是定位到底是哪一环出的问题。把 Codex 配好之后,你可以直接把VerifyImage.php、test.php和浏览器报错一起丢给它,让它按imagettftext、imagepng、点击切换的顺序逐项过,比自己在空白图和乱码之间反复试要快得多。
TaoToken 在这里就是给 Codex 供 Key 和 Base URL 的入口,验证码生成还是你自己的 PHP 在跑。配通之后,下次再遇到 GD 相关的怪问题,直接让 Codex 对照代码查就行。