二维码的本质是一种矩阵式条码符号,它本身只是数据的编码载体,而PHP作为服务端脚本语言,完全有能力在请求到达时动态生成二维码图片。所谓触发PHP代码执行,实际上就是让HTTP请求携带参数进入PHP脚本,脚本调用二维码生成库完成绘制,再以图片流的形式返回给浏览器。整个过程不依赖前端JavaScript,也不需要调用第三方付费接口,纯后端即可完成。

一、环境准备与二维码生成库的选择
在PHP中生成二维码,最常见的方案有三类:其一是传统的phpqrcode类库,单文件引入即可使用,适合老项目或不想使用composer的环境;其二是endroid/qr-code,这是composer生态下维护活跃的现代化库,支持圆点样式、Logo嵌入、渐变色等丰富特性;其三是借助GD库或ImageMagick自行绘制,这种方式代码量大,一般不推荐,除非有极其特殊的定制需求。
选择类库时需要考虑几个因素:PHP版本兼容性、是否需要美化样式、项目是否已使用composer管理依赖。phpqrcode基于GD库工作,要求PHP环境开启gd扩展,并且需要注意它原生只支持到PHP 5.x和部分7.x版本,在新版本PHP上可能需要社区修补版。endroid/qr-code则要求PHP 7.4以上,通过composer安装非常干净:
composer require endroid/qr-code
如果使用phpqrcode,下载后解压得到phpqrcode.php文件,直接require即可。无论哪种方案,都建议先执行php -m | grep gd确认GD扩展已安装,否则生成图片时会直接报错。此外,PHP的error_reporting设置如果过于宽松,警告信息混入图片二进制流会导致图片无法显示,这是新手最容易踩的坑之一。
二、通过URL参数触发二维码生成
触发执行的核心机制很简单:浏览器访问一个PHP文件,比如qrcode.php?text=hello,PHP脚本读取GET参数,将其编码为二维码并直接输出。这样二维码的URL本身就可以当作图片使用,可以直接放在img标签的src属性里,非常灵活。
下面是使用phpqrcode的完整示例,脚本接收text参数并即时输出PNG图片流:
<?php
// 引入phpqrcode类库
require_once 'phpqrcode/phpqrcode.php';
// 获取参数,未传时给默认值
$text = isset($_GET['text']) ? trim($_GET['text']) : 'https://ipipp.com';
// 对内容做长度校验,防止恶意超长请求
if (mb_strlen($text) > 1024) {
http_response_code(400);
exit('content too long');
}
// 设置输出头,告诉浏览器这是PNG图片
header('Content-Type: image/png');
// 参数依次为:输出内容、文件路径(false表示直接输出)、容错级别、尺寸、边距
QRcode::png($text, false, QR_ECLEVEL_M, 8, 2);
?>这段代码部署后,访问http://your-domain.com/qrcode.php?text=%E6%B5%8B%E8%AF%95就能看到二维码。注意中文内容必须经过URL编码传入,否则会出现乱码导致扫描结果错误。如果需要生成带中文的二维码,确保PHP文件本身保存为UTF-8编码,phpqrcode在输出前一般无需额外转码,但个别老版本需要先对内容做mb_convert_encoding处理。
使用endroid/qr-code的写法更加面向对象,代码可读性也更好:
<?php
require 'vendor/autoload.php';
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;
$text = $_GET['text'] ?? 'https://ipipp.com';
$qrCode = QrCode::create($text)
->setSize(300)
->setMargin(10);
$writer = new PngWriter();
$result = $writer->write($qrCode);
header('Content-Type: '.$result->getMimeType());
echo $result->getString();两种方式都实现了参数触发、即时生成、直接输出的闭环。前端页面只需这样引用即可:<img src="/qrcode.php?text=abc" alt="二维码">,二维码会随页面加载自动生成。
三、性能优化与安全加固技巧
动态生成意味着每次请求都要执行编码和绘图运算,在高并发场景下会带来CPU压力。因此缓存是必备技巧。最简单的做法是文件缓存:生成前先检查目标内容对应的图片文件是否已存在,存在就直接readfile输出,不存在才生成并落盘。以内容的md5值作为文件名即可实现天然的key去重:
$key = md5($text);
$cacheDir = __DIR__ . '/qrcode_cache/';
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
$file = $cacheDir . $key . '.png';
header('Content-Type: image/png');
if (is_file($file)) {
// 命中缓存,直接读取输出
readfile($file);
exit;
}
QRcode::png($text, $file, QR_ECLEVEL_M, 8, 2);
readfile($file);除了本地文件缓存,还可以在Nginx层做代理缓存,或配合Redis存储二进制内容,进一步降低磁盘IO。另外给响应头加上Cache-Control: max-age=86400,让浏览器和CDN也参与缓存,服务端压力会大幅下降。
安全方面必须重视三点。第一,永远不要把用户参数直接拼进文件路径,上面的md5方案已经规避了路径穿越风险;第二,对参数做长度和类型校验,防止超长字符串耗尽内存,二维码理论容量约2953字节,超出直接拒绝即可;第三,如果二维码内容会指向跳转链接,落地页要做白名单校验,避免被恶意利用生成钓鱼链接。此外,建议对开放生成接口做频率限制,例如基于session或IP的令牌桶,防止被刷量滥用。
四、业务场景扩展:带Logo与扫码统计
实际业务中,纯黑白二维码往往不够,比如支付场景需要中间嵌入商户Logo。phpqrcode生成后可以用GD函数把Logo图片叠加到二维码中央,注意容错级别要选H(30%纠错),否则Logo遮挡区域会导致扫描失败:
// 生成基础二维码到临时文件
QRcode::png($text, 'tmp.png', QR_ECLEVEL_H, 10, 2);
$qr = imagecreatefrompng('tmp.png');
$logo = imagecreatefrompng('logo.png');
// 将Logo缩放到二维码宽度的22%左右
$qrW = imagesx($qr);
$logoW = (int)($qrW * 0.22);
$logoH = (int)(imagesy($logo) * $logoW / imagesx($logo));
imagecopyresampled(
$qr, $logo,
(int)(($qrW - $logoW) / 2), (int)(($qrW - $logoH) / 2),
0, 0, $logoW, $logoH, imagesx($logo), imagesy($logo)
);
header('Content-Type: image/png');
imagepng($qr);
imagedestroy($qr);
imagedestroy($logo);另一个高频需求是扫码统计。实现思路是让二维码内容不直接指向目标地址,而是指向自家域名的中转接口,例如https://ipipp.com/go?id=123。用户扫码后请求先进入PHP脚本,脚本记录扫码时间、来源、设备信息到数据库或日志,然后再302跳转到真实目标地址。这样既拿到了统计数据,又保留了随时修改跳转目标的灵活性,营销活动里非常实用。
综合来看,PHP触发二维码生成的关键链路是:URL请求携带参数、脚本校验并调用类库、图片流输出、缓存与安全兜底。掌握这条链路后,无论是登录扫码、电子凭证还是内容分享,都可以在此基础上快速扩展,构建出稳定可靠的二维码服务。