参考——关于 PDO 的常见问题
Posted
技术标签:
【中文标题】参考——关于 PDO 的常见问题【英文标题】:Reference — frequently asked questions about PDO 【发布时间】:2013-04-06 03:09:39 【问题描述】:这是什么?
这是关于 php 数据对象的常见问题列表
这是为什么?
由于 PDO 有一些普通 PHP 用户不知道的特性,所以关于 PDO 中的准备语句和错误处理的问题非常常见。所以,这只是一个可以找到所有这些的地方。
我应该在这里做什么?
如果您的问题已与此列表密切相关,请在下面找到您的问题并将修复应用于您的代码。简要了解其他问题也是一个好主意,让自己为其他常见的陷阱做好准备。
名单
PDO query fails but I can't see any errors. How to get an error message from PDO? How can I use prepared statements with LIKE operator? How can I create a prepared statement for IN () operator? Can I use a PDO prepared statement to bind an identifier (a table or field name) or a syntax keyword? PDO prepared statement causes an error in LIMIT statement另见
Reference - What does this symbol mean in PHP? Reference - What does this error mean in PHP? Reference - What is variable scope, which variables are accessible from where and what are “undefined variable” errors?【问题讨论】:
我认为这不适合 SO。这不是一个常见问题解答网站。你可以把它放在你的网站上。至少帖子应该将所有权更改为“社区维基” 我发现一个 3 年 52k 代表的用户会发布这样的内容并附上 5 个自己的答案,这让我感到非常惊讶。您有足够的代表来编辑 PDO 标签 wiki。这属于那里。到处投反对票。标记为关闭。 您介意更改此问题的所有权以及社区 Wiki 的所有答案吗?这正是 CW 的意义所在。 仅供参考,这个问题目前正在meta.stackexchange.com/questions/176417/…进行讨论 拆分成单独的帖子,我想这些问题会非常有用。我支持创建规范或“大师”问题的努力,但我不得不同意这样的小组不符合 SO 的格式,就像如果所有这些问题都被一个人放到一个帖子中一样实际上是在寻找答案。 【参考方案1】: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();
【讨论】:
谢谢,但这只是且仅当限制在 PDO 中使用时?【参考方案2】:如何将预准备语句与 LIKE 运算符一起使用?
Prepared 语句可以表示仅表示完整的数据字面量。不是文字的一部分,也不是复杂的表达式,也不是标识符。但只能字符串或数字。因此,一个非常常见的陷阱是这样的查询:
$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('%',?,'%')";
虽然后者看起来太臃肿了。
【讨论】:
这应该是不言而喻的,但在大多数情况下,您需要在将$name
中的 %
、_
和任何其他引擎支持的通配符放入查询之前对其进行转义.
@DCoder:+1,只是为了完整起见,因为这是一个参考问题:addcslashes($name, '%_\\');
。【参考方案3】:
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 错误相同 - 所以,你可以使用站点范围的设置来定义行为 - 因此,您将获得没有这些无用代码的错误消息。 虽然无条件回显的错误消息可能会向潜在的攻击者透露一些敏感信息,但会造成混淆一个诚实的访客。
custom exception handler 可以稍后添加,但不是必需的。特别是对于新用户,建议使用未处理的异常,因为它们非常有用、有用且安全。 仅当您要自己处理错误时才使用try..catch
- 例如,回滚事务。
【讨论】:
以上是关于参考——关于 PDO 的常见问题的主要内容,如果未能解决你的问题,请参考以下文章