2.2. 连接并查询 MySQL 数据库
使用 PDO 及其预处理语句功能。
在 PHP 中,有许多方法可以连接到 MySQL 数据库。PDO (PHP 数据对象)是其中最新、最健壮的。它在多种不同类型的数据库之间具有一致的接口,使用面向对象的方式,支持更多新数据库提供的特性。
你应该使用 PDO 的预处理语句函数来帮助防止 SQL 注入攻击。使用 bindValue() 函数确保 SQL 免于一阶 SQL 注入攻击。(但这并不是100%安全的,更多细节请参阅 延伸阅读。)在过去,这必须通过一些难懂的 “魔术引号(magic quotes)” 函数组合来实现。PDO 使那堆东西变得不再必要。
范例
<?php
// 创建一个新连接。
// 你可能要用 localhost 替换第一个参数的 hostname。
// 注意我们如何将字符集声明为 utf8mb4。这会告知连接,我们将传递 UTF-8 格式的数据。根据你的配置这可能不是必选的,但如果你准备在数据库中存储 Unicode 字符串,这样做能为你减少很多麻烦。参见“要点”一节。
// 我们传入的 PDO 选项执行以下操作:
// PDO::ATTR_ERRMODE 允许在错误时抛出异常。这是个可选项,但相当有用。
// PDO::ATTR_PERSISTENT 禁用持续连接,因为在某些情况下会导致并发问题。参见"要点"一节。
$link = new PDO( 'mysql:host=your-hostname;dbname=your-db;charset=utf8mb4',
'your-username',
'your-password',
array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => false
)
);
$handle = $link->prepare('select Username from Users where UserId = ? or Username = ? limit ?');
$handle->bindValue(1, 100);
$handle->bindValue(2, 'Bilbo Baggins');
$handle->bindValue(3, 5);
$handle->execute();
// 如果同时查询行数过多,使用 fetchAll() 方法可能会占用大量资源。
// 在这种情况下,可以使用 fetch() 方法循环遍历每个结果行。
// 你还可以返回数组和其他格式,而不是对象类型。有关详情请参阅 PDO 文档。
$result = $handle->fetchAll(PDO::FETCH_OBJ);
foreach($result as $row){
print($row->Username);
}
要点
- 未将连接字符串中的字符集设置为 utf8mb4,可能会导致 Unicode 数据在数据库中存储不正确,这会取决于你的配置。
- 即使已经将字符集声明为 utf8mb4,也要确认实际的数据库表的确使用了 utf8mb4 字符集。关于为什么使用 utf8mb4 而不仅仅是 utf8,请查看 PHP and UTF-8 章节。
- 启用持久连接可能会导致奇怪的并发问题。这不是 PHP 层面的问题,而是应用程序级的问题。只要分析考虑过后果,持久连接也是可以安全使用的。请参阅 这个 Stack Overflow 问题。
延伸阅读
- PHP 手册: PDO
- 为什么要使用 PHP 的 PDO 访问数据库
- Stack Overflow: PHP PDO VS. 普通的 mysql_connect
- Stack Overflow:PDO 的预处理语句是否足以防范 SQL 注入?
- Stack Overflow:是否使用 “SET NAMES”
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: