52

这是什么?

这是关于 PHP 数据对象的常见问题列表

为什么是这样?

由于 PDO 有一些普通 PHP 用户不知道的特性,所以关于 PDO 中的准备语句和错误处理的问题非常常见。所以,这只是一个可以找到所有这些的地方。

我应该在这里做什么?

如果您的问题已与此列表密切相关,请在下面找到您的问题并将修复应用于您的代码。简要了解其他问题也是一个好主意,让自己为其他常见的陷阱做好准备。

名单

也可以看看

4

3 回答 3

24

PDO 查询失败,但我看不到任何错误。如何从 PDO 获取错误消息?

为了能够看到数据库错误,必须将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 错误相同 - 因此,您可以使用站点范围的设置来定义行为 - 因此,您将获得没有这些无用代码的错误消息。 虽然无条件回显的错误消息可能会向潜在的攻击者透露一些敏感信息,但会使诚实的访问者感到困惑。

于 2013-04-13T17:41:38.327 回答
21

PDO 准备好的语句导致 LIMIT 子句中的错误

出于兼容性目的,除非另有说明,否则 PDO 将仅通过用实际数据替换占位符来模拟准备好的语句,而不是将它们单独发送到服务器。并且使用“惰性”绑定(在 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();
    
于 2013-04-13T18:54:29.043 回答
9

如何将准备好的语句与 LIKE 运算符一起使用?

准备好的语句只能表示完整的数据字面量。不是文字的一部分,也不是复杂的表达式,也不是标识符。但无论是字符串还是数字。因此,一个非常常见的陷阱是这样的查询:

$sql = "SELECT * FROM t WHERE column LIKE '%?%'";

如果你稍微思考一下这个查询,你就会明白,在单引号内,问号变成了字面问号,对准备好的语句没有任何特殊含义。

因此,必须使用准备好的语句发送完整的字符串文字。有两种可能的方式:

  • 要么先准备完整的表达式:

    $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('%',?,'%')";
    

虽然后者似乎过于臃肿。

于 2013-04-13T17:51:27.267 回答