90%的新项目该用pdo,因其能规避sql注入、数据库迁移困难和错误静默失败三类硬坑;仅在需mysql特有功能或维护老系统时才选mysqli。

直接说结论:90%的新项目该用 PDO,不是因为“更高级”,而是它能让你少踩三类硬坑——SQL 注入、数据库迁移、错误静默失败。只有当你明确需要 MySQL 特有功能(比如异步查询、mysqli::real_connect() 连接复用)或维护纯 MySQL 老系统时,才该选 mysqli。
为什么 mysqli 的 connect_error 检查常被忽略
mysqli 默认不抛异常,出错只设 $mysqli->connect_error 和 $mysqli->connect_errno,页面空白、日志无记录是常态。
- 必须手动加
if ($mysqli->connect_error) { die(...) },漏一次就埋雷 - 面向过程写法(
mysqli_connect())返回false,但新手常直接当对象用,调->query()报致命错误 -
set_charset("utf8")不等于set_charset("utf8mb4"),后者才真正支持 emoji,但很多教程仍写前者
PDO 的预处理默认是“假的”?
是的。PDO::ATTR_EMULATE_PREPARES 默认为 true,意味着 SQL 是 PHP 拼好再发给 MySQL 的——这会让 WHERE name LIKE ? 传入 '%abc%' 直接报错。
- 修复方式:连接后立刻执行
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false) - 一旦关掉模拟预处理,
LIKE ?就能直接传带通配符的值,但要注意云数据库(如阿里云 RDS)可能默认禁用服务端 prepare,此时会静默退化回模拟模式,且不警告 -
bindValue()和bindParam()区别关键在引用:后者绑定变量本身,适合循环中复用同一语句;前者绑定当前值,更安全
混用 mysqli 和 PDO 会出什么问题
不会语法报错,但会引发资源泄漏和逻辑断裂——这是最隐蔽也最难调试的一类问题。
- 一个请求里同时打开
mysqli连接和PDO连接,却只关其中一个,MySQL 连接数缓慢爬升直至拒绝服务 - 用
PDO执行事务,中间穿插mysqli_query(),事务隔离失效,数据不一致 -
PDO::lastInsertId()在多库场景下可能返回错库的 ID;而$mysqli->insert_id绑定在连接实例上,更确定
字符集与错误模式必须在 new 之后立刻设
这两个设置不是“可有可无的优化”,而是影响行为的关键开关,且必须在 new PDO() 或 new mysqli() 后立即生效。
-
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)必须紧跟new PDO(),封装进工厂函数再 set 就无效 -
$mysqli->set_charset("utf8mb4")必须在query()前调用,否则中文存成问号,且不会报错 - 连接字符串里的
charset=utf8mb4(PDO)和代码里的set_charset()(mysqli)不是二选一,而是互补:前者影响连接初始化,后者影响后续查询上下文
真正麻烦的不是选错扩展,而是把连接、预处理、字符集、错误处理这四件事拆开配置——它们本是一体的。PDO 把这四件事收束到统一接口里,mysqli 则要求你每一步都手动校验。项目越往后走,这种差异越明显。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











