Skip to content

MySQL 连接

现代 PHP 应用主要使用以下两个扩展与 MySQL 数据库交互:

  • MySQLi: 专门为 MySQL 设计的改进扩展。提供面向对象和面向过程两种接口。
  • PDO (PHP Data Objects): 一个一致的数据库抽象层,支持多种数据库系统(包括 MySQL)。主要为面向对象接口。

旧的 mysql_* 扩展已被弃用,并且由于存在安全漏洞和缺乏现代功能,不应在新代码中使用。

选择通常取决于项目需求:

  • PDO: 如果你预计将来可能需要切换到其他数据库系统(例如 PostgreSQL、SQLite),则推荐使用。它为不同的数据库提供了统一的 API,使得迁移更容易。它是严格的面向对象。
  • MySQLi: 如果你确定应用程序只使用 MySQL,则非常适合。一些开发者可能偏爱它更直接地映射到 MySQL 特有功能,或者喜欢其可选的面向过程风格。
  • 两者: 都支持预处理语句(对安全至关重要)、事务和现代 MySQL 功能。

对于大多数新项目来说,因其灵活性和抽象性,PDO 通常更受青睐。

这两个扩展通常包含在 PHP 中,但可能需要手动启用。

  • Linux 系统(包管理器): 通常由 php-mysql 包提供(该包通常会安装 MySQLi 和 PDO_MySQL)。示例:sudo apt-get install php-mysql 或 sudo yum install php-mysqlnd。重启你的 Web 服务器/PHP-FPM。
  • Windows 系统: 检查你的 php.ini 文件。取消注释或添加类似 extension=mysqli 和 extension=pdo_mysql 的行。确保 extension_dir 设置正确。重启你的 Web 服务器。
  • 检查: 使用 php -m 或 phpinfo() 来验证 mysqli 和 pdo_mysql 是否被列为已启用的模块。

请参阅你的环境文档以获取具体信息。

在与数据库交互之前,必须建立连接。

最佳实践:安全地存储数据库凭据(例如,使用环境变量或放置在 Web 根目录之外的配置文件),而不是直接写在代码中。

<?php
$servername = getenv('DB_HOST') ?: "localhost";
$username = getenv('DB_USER') ?: "your_username";
$password = getenv('DB_PASS') ?: "your_password";
$dbname = getenv('DB_NAME') ?: "myDB";
// 创建连接
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // 将错误报告为异常
try {
$conn = new mysqli($servername, $username, $password, $dbname);
// 设置字符集 - 非常重要
$conn->set_charset("utf8mb4");
echo "Connected successfully using MySQLi (OO)"; // 使用 MySQLi (OO) 连接成功
} catch (mysqli_sql_exception $e) {
// 处理连接错误
// 在生产环境中应记录错误日志,而不是回显详细信息
error_log("MySQLi Connection Error: " . $e->getMessage());
die("Database connection failed. Please try again later."); // 友好提示用户
}
// 连接对象 ($conn) 现在已准备好进行查询
// ... 执行查询 ...
// 关闭连接(可选,PHP 在脚本结束时会自动关闭)
// $conn->close();
?>
<?php
$servername = getenv('DB_HOST') ?: "localhost";
$username = getenv('DB_USER') ?: "your_username";
$password = getenv('DB_PASS') ?: "your_password";
$dbname = getenv('DB_NAME') ?: "myDB";
// 将错误报告为异常
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
// 创建连接
$conn = mysqli_connect($servername, $username, $password, $dbname);
// 设置字符集 - 非常重要
mysqli_set_charset($conn, "utf8mb4");
echo "Connected successfully using MySQLi (Procedural)"; // 使用 MySQLi (面向过程) 连接成功
} catch (mysqli_sql_exception $e) {
// 处理连接错误
error_log("MySQLi Connection Error: " . $e->getMessage());
die("Database connection failed. Please try again later.");
}
// 连接资源 ($conn) 现在已准备好进行查询
// ... 执行查询 ...
// 关闭连接(可选)
// mysqli_close($conn);
?>
<?php
$host = getenv('DB_HOST') ?: 'localhost';
$db = getenv('DB_NAME') ?: 'myDBPDO';
$user = getenv('DB_USER') ?: 'your_username';
$pass = getenv('DB_PASS') ?: 'your_password';
$charset = 'utf8mb4'; // 推荐的字符集
// Data Source Name (DSN) 数据源名称
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 以异常形式开启错误报告
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 默认获取模式为关联数组
PDO::ATTR_EMULATE_PREPARES => false, // 出于安全和性能考虑禁用模拟
];
try {
// 创建 PDO 实例
$pdo = new PDO($dsn, $user, $pass, $options);
echo "Connected successfully using PDO"; // 使用 PDO 连接成功
} catch (PDOException $e) {
// 处理连接错误
error_log("PDO Connection Error: " . $e->getMessage());
// throw new PDOException($e->getMessage(), (int)$e->getCode()); // 或重新抛出异常
die("Database connection failed. Please try again later.");
}
// PDO 对象 ($pdo) 现在已准备好进行查询
// ... 执行查询 ...
// 关闭连接(可选,会自动发生)
// $pdo = null;
?>

连接示例中的要点:

  • 错误处理: 使用 try...catch 块是现代方式。MySQLi 需要 mysqli_report() 才能抛出异常;PDO 则通过 ATTR_ERRMODE 选项实现。
  • 凭据: 通过 getenv() 获取凭据是外部配置的一个示例。
  • 字符集: 明确将字符集设置为 utf8mb4(通过 $conn->set_charset() 或在 DSN/选项中设置)对于正确处理各种字符(包括 emoji)至关重要。
  • PDO 选项: 推荐将 PDO::ATTR_ERRMODE 设置为 PDO::ERRMODE_EXCEPTION,设置 PDO::ATTR_DEFAULT_FETCH_MODE 以方便使用,以及禁用 PDO::ATTR_EMULATE_PREPARES。

在典型的 Web 请求场景中,当脚本执行完毕时,PHP 会自动关闭数据库连接。通常无需显式关闭连接。

然而,在长时间运行的脚本或资源受限的环境中,一旦不再需要连接就立即关闭是一个好习惯:

  • MySQLi (OO): $conn->close();
  • MySQLi (面向过程): mysqli_close($conn);
  • PDO: $pdo = null;(将对象变量设置为 null 会移除引用,允许 PHP 的垃圾回收器关闭连接)。

上面的示例展示了这些调用通常放置的位置(通常被注释掉了)。