参考-关于PDO的常见问题


Reference — frequently asked questions about PDO

这是什么?

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

为什么?

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

我该怎么做?

如果您的问题在此列表中被淘汰,请在下面找到您的问题并将修复应用于您的代码。对其他问题做一个简短的浏览也是一个好主意,让你自己为其他常见的陷阱做好准备。

列表
  • PDO查询失败,但我看不到任何错误。如何从PDO获得错误消息?
  • 我如何使用LIKE操作符预处理语句?
  • 如何为IN()操作符创建准备好的语句?
  • 我可以使用PDO准备语句绑定标识符(表名或字段名)或语法关键字吗?
  • PDO准备语句导致LIMIT语句错误

参见

    这个符号在PHP中是什么意思?这个错误在PHP中意味着什么?
  • 参考-什么是变量范围,哪些变量可以从哪里访问,什么是"未定义变量"错误?

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

为了能够看到数据库错误,必须将PDO errmode设置为exceptions。异常在很多方面都比常规错误要好:它们总是包含堆栈跟踪,可以使用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错误相同的方式运行-因此,您可以使用站点范围的设置定义行为-因此,您将获得没有这些无用代码的错误消息。虽然无条件回显错误消息可能会向潜在的攻击者泄露一些敏感信息,但会混淆诚实的访问者。

    自定义异常处理程序可以稍后添加,但不是必需的。特别是对于新用户,建议使用未处理的异常,因为它们非常有用、有用且安全。
  • 使用try..catch只有当你要处理错误本身-说,回滚一个事务。

PDO预处理语句导致LIMIT子句错误

出于兼容性目的,PDO将通过用实际数据替换占位符来模拟预处理语句,而不是将它们单独发送到服务器,除非另有说明。使用"惰性"绑定(在execute()中使用array), 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();
    

如何使用LIKE操作符的准备语句?

预处理语句只能表示完整的数据文字。不是文字的一部分,不是复杂的表达式,也不是标识符。但是字符串或仅数字。一个很常见的陷阱是这样的查询:

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

虽然后者看起来过于臃肿