这是有关PHP数据对象的常见问题列表
由于PDO具有常规PHP用户不知道的某些功能,因此在PDO中有关预准备语句和错误处理的问题非常频繁。所以,这只是一个可以找到它们的地方。
如果您的问题已与此列表密切相关,请在下面找到您的问题并将修复程序应用于您的代码。对其他问题进行简要介绍也是一个好主意,让自己为其他常见陷阱做好准备。
另见
答案 0 :(得分:24)
为了能够查看数据库错误,必须将PDO errmode设置为异常。异常在许多方面优于常规错误:它们总是包含堆栈跟踪,可以使用try..catch捕获它们或使用专用错误处理程序处理它们。甚至未处理,它们充当常规PHP错误,提供所有重要信息,遵循站点范围的错误报告设置。
请注意,将此模式设置为连接选项会让PDO也会对连接错误抛出异常,这非常重要 因此,这是一个以正确方式创建PDO连接的示例:
$dsn = "mysql:host=$host;dbname=$db;charset=utf8";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// other options
);
$pdo = new PDO($dsn, $user, $pass, $opt);
以这种方式连接,将始终通知您在查询执行期间发生的所有数据库错误。请注意,您必须能够查看PHP错误。在实际站点上,您必须查看错误日志,因此,必须设置
error_reporting(E_ALL);
ini_set('display_errors',0);
ini_set('log_errors',1);
在本地开发服务器上,可以在屏幕上显示错误:
error_reporting(E_ALL);
ini_set('display_errors',1);
当然,您永远不应该在PDO语句前使用错误抑制运算符(@
)。
此外,由于许多不良示例告诉您将每个PDO语句包装到try..catch
块中,我必须做一个明确的说明:
请勿使用try..catch运算符来回显错误消息。未捕获的异常已经很好用于此目的,因为它的行为方式与其他PHP错误相同 - 所以,你可以使用站点范围的设置来定义行为 - 因此,您将收到错误消息而没有此无用的代码。 虽然无条件回显的错误消息可能会向潜在的攻击者泄露一些敏感信息,但会混淆一个诚实的访客。
- 以后可以添加custom exception handler,但不是必需的。特别是对于新用户,建议使用未处理的异常,因为它们提供的信息非常丰富,有用且安全。
- 仅当您要处理错误本身时才使用
try..catch
- 例如,回滚事务。
答案 1 :(得分:19)
出于兼容性目的,PDO将通过用实际数据替换占位符来模拟预备语句,而不是单独将它们发送到服务器,除非另有说明。使用“lazy”绑定(在execute()中使用数组),PDO会将每个参数视为字符串。因此,准备好的LIMIT ?,?
查询变为LIMIT '10', '10'
,这是导致查询失败的无效语法。
这个问题可以解决
关闭仿真模式(因为MySQL可以正确排序所有占位符):
$conn->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
通过绑定并明确设置正确的类型(PDO :: PARAM_INT):
$stm = $pdo->prepare('SELECT * FROM table LIMIT ?, ?');
$stm->bindValue(1, $limit_from,PDO::PARAM_INT);
$stm->bindValue(2, $per_page,PDO::PARAM_INT);
$stm->execute();
$data = $stm->fetchAll();
答案 2 :(得分:9)
准备好的声明只能代表完整数据文字。不是文字的一部分,也不是复杂的表达,也不是标识符。但字符串或仅限数字。因此,一个非常常见的陷阱是这样的查询:
$sql = "SELECT * FROM t WHERE column LIKE '%?%'";
如果你稍微思考一下这个问题,你就会明白,在单引号里面,问号会成为一个文字问号,对准备好的语句没有任何特殊意义。
因此,必须使用预准备语句发送完整的字符串文字。有两种可能的方式:
首先准备FULL表达式:
$name = "%$name%";
$stm = $pdo->prepare("SELECT * FROM table WHERE name LIKE ?");
$stm->execute(array($name));
$data = $stm->fetchAll();
或在查询中使用连接
$sql = "SELECT * FROM t WHERE column LIKE concat('%',?,'%')";
虽然后者看起来太臃肿了。