深入浅出:PHP 数据库连接与操作(MySQLi 和 PDO)

在 Web 开发中,数据库是存储和管理数据的核心组件。无论是用户信息、文章内容还是订单记录,几乎所有现代 Web 应用都依赖于数据库来保存和检索数据。PHP 提供了多种方式来连接和操作数据库,其中最常用的两种方法是 MySQLiPDO(PHP Data Objects)。本文将深入探讨这两种方式,帮助你理解它们的优缺点,并通过实际案例展示如何在项目中使用它们。

什么是 MySQLi?

MySQLi 是 PHP 内置的一个扩展,专门用于与 MySQL 数据库进行交互。它提供了两种编程接口:面向过程的 API 和面向对象的 API。MySQLi 支持预处理语句、事务处理以及多种安全特性,使得它成为处理 MySQL 数据库的理想选择。

MySQLi 的特点

  1. 支持多种连接方式:MySQLi 可以通过常规连接(mysqli_connect())或持久连接(mysqli_pconnect())与数据库建立连接。
  2. 预处理语句:MySQLi 支持预处理语句,可以有效防止 SQL 注入攻击。
  3. 多结果集支持:MySQLi 支持从存储过程返回多个结果集。
  4. 面向对象和面向过程的 API:开发者可以根据自己的喜好选择使用面向对象的方式或面向过程的方式。

使用 MySQLi 连接数据库

面向过程的方式

使用 mysqli_connect() 函数可以创建一个到 MySQL 数据库的连接。如果连接成功,函数将返回一个连接标识符;如果失败,则返回 false

<?php
$host = "localhost";
$username = "root";
$password = "password";
$database = "test_db";

// 创建连接
$conn = mysqli_connect($host, $username, $password, $database);

// 检查连接是否成功
if (!$conn) {
    die("Connection failed: " . mysqli_connect_error());
}
echo "Connected successfully";

// 关闭连接
mysqli_close($conn);
?>

在这个例子中,我们使用 mysqli_connect() 函数连接到名为 test_db 的数据库,并在连接成功后输出一条消息。最后,我们使用 mysqli_close() 函数关闭连接。

面向对象的方式

如果你更喜欢面向对象的编程风格,可以使用 mysqli 类来创建连接。

<?php
$host = "localhost";
$username = "root";
$password = "password";
$database = "test_db";

try {
    // 创建连接
    $conn = new mysqli($host, $username, $password, $database);

    // 检查连接是否成功
    if ($conn->connect_error) {
        throw new Exception("Connection failed: " . $conn->connect_error);
    }
    echo "Connected successfully";

    // 关闭连接
    $conn->close();
} catch (Exception $e) {
    echo "Error: " . $e->getMessage();
}
?>

在这个例子中,我们使用 new mysqli() 创建了一个 mysqli 对象,并通过 connect_error 属性检查连接是否成功。如果连接失败,抛出异常并捕获处理。

使用 MySQLi 执行查询

简单查询

我们可以使用 mysqli_query() 函数执行简单的 SQL 查询。该函数返回一个结果集对象,可以通过 mysqli_fetch_assoc() 函数获取每一行的数据。

<?php
$host = "localhost";
$username = "root";
$password = "password";
$database = "test_db";

// 创建连接
$conn = mysqli_connect($host, $username, $password, $database);

// 执行查询
$result = mysqli_query($conn, "SELECT * FROM users");

// 获取并输出每一行数据
while ($row = mysqli_fetch_assoc($result)) {
    echo "ID: " . $row['id'] . ", Name: " . $row['name'] . "<br>";
}

// 关闭连接
mysqli_close($conn);
?>

在这个例子中,我们执行了一个 SELECT 查询,获取 users 表中的所有数据,并逐行输出每条记录的 idname 字段。

预处理语句

为了防止 SQL 注入攻击,推荐使用预处理语句。预处理语句将 SQL 语句与数据分离,确保用户输入不会直接影响查询的执行。

<?php
$host = "localhost";
$username = "root";
$password = "password";
$database = "test_db";

// 创建连接
$conn = new mysqli($host, $username, $password, $database);

// 准备 SQL 语句
$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");

// 绑定参数
$id = 1;
$stmt->bind_param("i", $id);

// 执行查询
$stmt->execute();

// 获取结果
$result = $stmt->get_result();

// 输出结果
while ($row = $result->fetch_assoc()) {
    echo "ID: " . $row['id'] . ", Name: " . $row['name'] . "<br>";
}

// 关闭连接
$stmt->close();
$conn->close();
?>

在这个例子中,我们使用 prepare() 方法准备了一个带有占位符的 SQL 语句,然后通过 bind_param() 方法绑定参数,确保用户输入的安全性。最后,我们执行查询并输出结果。

什么是 PDO?

PDO(PHP Data Objects)是一个轻量级的数据库抽象层,允许你在不同的数据库系统之间轻松切换,而不需要修改大量代码。PDO 支持多种数据库驱动,包括 MySQL、PostgreSQL、SQLite 等。与 MySQLi 相比,PDO 更加灵活,适合需要跨数据库兼容性的项目。

PDO 的特点

  1. 数据库抽象层:PDO 提供了一个统一的接口,可以在不同数据库之间轻松切换,而不需要修改代码。
  2. 预处理语句:PDO 支持预处理语句,可以有效防止 SQL 注入攻击。
  3. 错误处理:PDO 提供了更强大的错误处理机制,支持抛出异常(PDOException),便于调试和日志记录。
  4. 面向对象的 API:PDO 只提供面向对象的 API,没有面向过程的版本。

使用 PDO 连接数据库

要使用 PDO 连接到 MySQL 数据库,首先需要创建一个 PDO 对象,并指定数据库的 DSN(数据源名称)、用户名和密码。

<?php
$dsn = "mysql:host=localhost;dbname=test_db";
$username = "root";
$password = "password";

try {
    // 创建连接
    $pdo = new PDO($dsn, $username, $password);

    // 设置 PDO 错误模式为抛出异常
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    echo "Connected successfully";
} catch (PDOException $e) {
    echo "Connection failed: " . $e->getMessage();
}
?>

在这个例子中,我们使用 new PDO() 创建了一个 PDO 对象,并通过 setAttribute() 方法设置了错误模式为抛出异常。如果连接失败,会抛出 PDOException 异常并被捕获处理。

使用 PDO 执行查询

简单查询

我们可以使用 query() 方法执行简单的 SQL 查询。该方法返回一个 PDOStatement 对象,可以通过 fetch()fetchAll() 方法获取查询结果。

<?php
$dsn = "mysql:host=localhost;dbname=test_db";
$username = "root";
$password = "password";

try {
    // 创建连接
    $pdo = new PDO($dsn, $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // 执行查询
    $stmt = $pdo->query("SELECT * FROM users");

    // 获取并输出每一行数据
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        echo "ID: " . $row['id'] . ", Name: " . $row['name'] . "<br>";
    }

    // 关闭连接
    $pdo = null;
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}
?>

在这个例子中,我们执行了一个 SELECT 查询,获取 users 表中的所有数据,并逐行输出每条记录的 idname 字段。

预处理语句

与 MySQLi 类似,PDO 也支持预处理语句。预处理语句可以有效防止 SQL 注入攻击,并提高查询性能。

<?php
$dsn = "mysql:host=localhost;dbname=test_db";
$username = "root";
$password = "password";

try {
    // 创建连接
    $pdo = new PDO($dsn, $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // 准备 SQL 语句
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");

    // 绑定参数
    $id = 1;
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);

    // 执行查询
    $stmt->execute();

    // 获取结果
    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    // 输出结果
    if ($row) {
        echo "ID: " . $row['id'] . ", Name: " . $row['name'] . "<br>";
    } else {
        echo "No user found.";
    }

    // 关闭连接
    $pdo = null;
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}
?>

在这个例子中,我们使用 prepare() 方法准备了一个带有命名占位符的 SQL 语句,然后通过 bindParam() 方法绑定参数,确保用户输入的安全性。最后,我们执行查询并输出结果。

PDO 的优势

  1. 跨数据库兼容性:PDO 支持多种数据库驱动,可以在不同数据库之间轻松切换,而不需要修改大量代码。
  2. 更好的错误处理:PDO 提供了更强大的错误处理机制,支持抛出异常,便于调试和日志记录。
  3. 预处理语句:PDO 支持预处理语句,可以有效防止 SQL 注入攻击。
  4. 面向对象的 API:PDO 只提供面向对象的 API,代码更加简洁和易读。

MySQLi 与 PDO 的比较

特性MySQLiPDO
数据库支持仅支持 MySQL支持多种数据库(MySQL、PostgreSQL、SQLite等)
API 风格面向过程和面向对象仅面向对象
错误处理返回错误代码或布尔值抛出异常(PDOException
预处理语句支持支持
多结果集支持支持支持
跨数据库兼容性仅限 MySQL支持多种数据库

选择 MySQLi 还是 PDO?

  • 如果你只使用 MySQL,并且希望使用更简单的 API,可以选择 MySQLi。MySQLi 提供了面向过程和面向对象的两种 API,代码编写更加直观。
  • 如果你需要跨数据库兼容性,或者希望使用更强大的错误处理机制,建议选择 PDO。PDO 支持多种数据库,并且提供了更灵活的 API 和更好的安全性。

综合案例:用户注册与登录系统

为了更好地理解 MySQLi 和 PDO 的实际应用,我们来实现一个简单的用户注册与登录系统。这个系统将使用 PDO 进行数据库操作,因为它的跨数据库兼容性和更好的错误处理机制更适合大型项目。

数据库表结构

假设我们有一个 users 表,包含以下字段:

  • id:用户 ID(主键,自增)
  • username:用户名
  • password:密码(加密存储)
  • email:电子邮件

用户注册功能

用户注册时,我们需要检查用户名是否已存在,如果不存在则将用户信息插入数据库。

<?php
$dsn = "mysql:host=localhost;dbname=test_db";
$username = "root";
$password = "password";

try {
    // 创建连接
    $pdo = new PDO($dsn, $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // 获取用户输入
    $username = $_POST['username'];
    $password = password_hash($_POST['password'], PASSWORD_BCRYPT);
    $email = $_POST['email'];

    // 检查用户名是否已存在
    $stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
    $stmt->bindParam(':username', $username);
    $stmt->execute();

    if ($stmt->rowCount() > 0) {
        echo "Username already exists.";
    } else {
        // 插入新用户
        $stmt = $pdo->prepare("INSERT INTO users (username, password, email) VALUES (:username, :password, :email)");
        $stmt->bindParam(':username', $username);
        $stmt->bindParam(':password', $password);
        $stmt->bindParam(':email', $email);
        $stmt->execute();

        echo "Registration successful!";
    }

    // 关闭连接
    $pdo = null;
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}
?>

在这个例子中,我们首先检查用户名是否已存在,如果不存在则将用户信息插入数据库。密码使用 password_hash() 函数进行加密存储,确保安全性。

用户登录功能

用户登录时,我们需要验证用户名和密码是否匹配。如果匹配,则允许用户登录。

<?php
$dsn = "mysql:host=localhost;dbname=test_db";
$username = "root";
$password = "password";

try {
    // 创建连接
    $pdo = new PDO($dsn, $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // 获取用户输入
    $username = $_POST['username'];
    $password = $_POST['password'];

    // 查询用户信息
    $stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
    $stmt->bindParam(':username', $username);
    $stmt->execute();

    $user = $stmt->fetch(PDO::FETCH_ASSOC);

    if ($user && password_verify($password, $user['password'])) {
        echo "Login successful!";
    } else {
        echo "Invalid username or password.";
    }

    // 关闭连接
    $pdo = null;
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}
?>

在这个例子中,我们使用 password_verify() 函数验证用户输入的密码是否与数据库中存储的加密密码匹配。如果匹配,则允许用户登录。

总结

在 PHP 中,MySQLi 和 PDO 是两种常用的数据库连接方式。MySQLi 专注于 MySQL 数据库,提供了简单易用的 API,适合小型项目;而 PDO 则是一个更通用的数据库抽象层,支持多种数据库,并提供了更强大的功能和更好的安全性。根据你的项目需求,可以选择合适的方式来连接和操作数据库。

通过本文的学习,你应该已经掌握了如何使用 MySQLi 和 PDO 连接 MySQL 数据库,并执行各种查询操作。希望这些知识能帮助你在未来的项目中写出更加安全、高效的代码。

参考资料

欢迎在评论区互动,彼此交流相互学习! 😊

更多推荐