pdo扩展未启用或连接失败主因是php.ini未开启pdo_mysql或dsn缺失charset=utf8mb4;需用php -m | grep pdo验证双模块启用,dsn必须显式声明charset=utf8mb4,禁用port=3306和变量拼接,强制使用prepare+execute防注入,大数据量用fetch流式处理,必须设置errmode_exception并配try/catch+rollback。

PDO 扩展没启用或连接失败,八成是 extension=pdo_mysql 没在 php.ini 里打开,或者 DSN 中漏了 charset=utf8mb4 —— 这俩问题占实际部署故障的 70% 以上。
怎么确认 PDO 和 pdo_mysql 都已启用
别靠猜。直接运行命令:
php -m | grep pdo
必须同时看到 pdo 和 pdo_mysql。如果只看到 pdo,说明驱动没加载;如果啥都没,连基础扩展都关了。
另一个验证方式:写个 phpinfo.php 放到 Web 目录下,浏览器访问,搜索 “PDO” 和 “mysql”,确认两处都显示“enabled”。
Windows 下注意检查 php.ini 是否改对了文件(常见错误:改了 CLI 的 php.ini,但 Web 用的是 Apache 或 Nginx 加载的另一份)。
DSN 必须显式带 charset=utf8mb4
只在连接后执行 $pdo->exec("SET NAMES utf8mb4") 是靠不住的,尤其在连接池、长连接或复用连接时,字符集可能被重置或不生效。
-
mysql:host=localhost;dbname=testdb❌ 缺少编码,中文入库变问号或乱码 -
mysql:host=localhost;dbname=testdb;charset=utf8mb4✅ 正确写法 - 不要写
port=3306—— MySQL 客户端库默认就是 3306,硬加反而在某些环境下触发 DNS 解析失败或连接超时 - 避免在 DSN 里拼接变量,比如
"mysql:host=$host;dbname=$db",万一变量含特殊字符(如分号、等号)会直接破坏 DSN 解析
prepare + execute 是防注入唯一靠谱路径
哪怕你把 $_GET['id'] 强转成 (int),也挡不住字段名、ORDER BY、UNION SELECT 等上下文绕过。预处理不是“可选优化”,是底线要求。
- 值绑定只支持
?(位置参数)或:name(命名参数),不支持表名、字段名、GROUP BY 子句 —— 这些必须走白名单校验后硬拼 -
bindValue()和bindParam()区别在于:前者传值,后者传引用;99% 场景用bindValue()更安全,避免后续变量改写影响语句 - 批量插入别拼大 SQL:
INSERT INTO t VALUES (1,'a'),(2,'b'),...,内存爆炸且易超 max_allowed_packet;改用循环execute()或分批(每 200 行一次) -
execute(['active', '2024-01-01'])中的字符串和日期,PDO 会按绑定类型自动处理,不用手动转strtotime()或intval()
fetch()、fetchAll()、while + fetch() 怎么选
不是语法问题,是内存和场景问题。取 10 行数据用 fetchAll() 没压力;取 10 万行还用它,PHP 进程直接 OOM。
-
fetch():适合单行结果(如SELECT COUNT(*)),或配合while流式读取大结果集 -
fetchAll():适合小结果集(PDO::FETCH_ASSOC 避免冗余 -
while ($row = $stmt->fetch()):大数据量、只读一遍、边取边处理(如导出 CSV、写日志),内存最稳 - 别在循环里反复调用
fetch()却忘了closeCursor()—— 某些驱动下会导致下一条语句执行失败
最容易被忽略的其实是错误模式和事务兜底:不设 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,SQL 错误就静默失败;不配 try/catch + rollback(),事务中途出错数据就卡在半途。这两点不落实,其他都白搭。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











