PHP + MySQL 基础教程
本教程将带你完成以下任务:

- 理解环境:了解你需要什么软件。
- 连接数据库:学习如何用 PHP 连接到 MySQL。
- 执行 SQL:学习如何执行增、删、改、查 操作。
- 显示数据:将数据库中的数据显示在网页上。
- 安全第一:学习如何防止 SQL 注入攻击。
- 完整示例:构建一个简单的留言板。
第一部分:准备工作
在开始之前,你需要一个本地开发环境,能够运行 PHP 和 MySQL,最简单的方法是安装一个集成环境包。
推荐软件:XAMPP 或 WAMP
- XAMPP (跨平台): 支持 Windows, macOS, Linux,非常流行且易于安装。
- WAMP (仅限 Windows): Windows 用户的另一个绝佳选择。
安装步骤:
安装后验证:
- 启动 XAMPP Control Panel。
- 确保 Apache 和 MySQL 模块的 Start 按钮被按下(即服务已启动)。
- 打开浏览器,访问
http://localhost或http://127.0.0.1,如果你能看到 XAMPP 的欢迎页面,说明 Apache 运行正常。 - 访问
http://localhost/phpmyadmin,如果你能看到 phpMyAdmin 的管理界面,说明 MySQL 和相关工具也运行正常。
第二部分:创建数据库和表
我们将使用 phpMyAdmin 来创建一个数据库和一张表。

-
在浏览器中打开
http://localhost/phpmyadmin。 -
在首页的 "创建数据库" 输入框中,输入数据库名
my_blog,然后点击 "创建"。 -
在左侧选择刚刚创建的
my_blog数据库。 -
在 "在数据库 my_blog 中创建表" 输入框中,输入表名
posts,列数设为3,然后点击 "执行"。
(图片来源网络,侵删) -
现在来定义表的结构:
-
列名:
id -
类型:
INT -
长度/值:
11 -
属性:
UNSIGNED(无符号) -
索引:
PRIMARY(主键) -
A_I:勾选
AUTO_INCREMENT(自动递增) -
列名:
title -
类型:
VARCHAR -
长度/值:
255 -
列名:
content -
类型:
TEXT
-
-
点击右下角的 "保存" 按钮。
你有了一个名为 my_blog 的数据库,里面有一张名为 posts 的表,可以存储文章的 ID、标题和内容。
第三部分:PHP 连接 MySQL
这是 PHP 与 MySQL 通信的第一步,我们将创建一个配置文件,方便管理数据库连接信息。
文件:config.php
<?php
// 数据库连接参数
$db_host = 'localhost'; // 数据库主机名
$db_user = 'root'; // 数据库用户名
$db_pass = ''; // 数据库密码 (XAMPP/WAMP 默认为空)
$db_name = 'my_blog'; // 数据库名
// 创建连接
$conn = new mysqli($db_host, $db_user, $db_pass, $db_name);
// 检查连接是否成功
if ($conn->connect_error) {
// 如果连接失败,终止脚本并显示错误信息
die("连接失败: " . $conn->connect_error);
}
// 设置字符集,防止中文乱码
$conn->set_charset("utf8mb4");
?>
代码解释:
new mysqli(...): 创建一个新的 MySQLi 对象,尝试连接到数据库。$conn->connect_error: 如果连接失败,这个属性会包含错误信息。die(...): 终止脚本执行并打印错误信息,这对于调试非常有用。$conn->set_charset("utf8mb4"): 非常重要! 设置数据库连接的字符集为utf8mb4,它可以完美支持包括 Emoji 在内的所有 Unicode 字符,避免乱码问题。
第四部分:执行 SQL 查询 (CRUD 操作)
现在我们已经连接到数据库,可以执行各种 SQL 操作了,CRUD 代表 Create (创建), Read (读取), Update (更新), Delete (删除)。
创建数据
文件:create_post.php
<?php
require_once 'config.php'; // 引入配置文件
// 1. 准备 SQL 语句 (使用 ? 作为占位符)
$sql = "INSERT INTO posts (title, content) VALUES (?, ?)";
// 2. 准备预处理语句
$stmt = $conn->prepare($sql);
// 3. 绑定参数
// "ss" 表示两个参数都是字符串
// $stmt->bind_param("类型", 变量1, 变量2, ...);
$title = "我的第一篇 PHP 博客";
$content = "这是使用 PHP 和 MySQL 写下的第一篇内容,感觉真棒!";
$stmt->bind_param("ss", $title, $content);
// 4. 执行预处理语句
if ($stmt->execute()) {
echo "新记录插入成功!";
// 获取最后插入的 ID
echo "ID: " . $stmt->insert_id;
} else {
echo "Error: " . $sql . "<br>" . $stmt->error;
}
// 5. 关闭语句
$stmt->close();
// 6. 关闭连接
$conn->close();
?>
代码解释:
- 预处理语句:这是防止 SQL 注入攻击的标准做法,我们先发送 SQL 语句的模板,然后单独发送数据。
prepare(): 准备 SQL 语句模板。bind_param(): 将变量绑定到 SQL 语句中的 占位符。- 第一个参数
"ss"是一个类型字符串,s代表 string (字符串),i代表 integer (整数),d代表 double (浮点数)。 - 后面的参数是你要绑定的 PHP 变量。
- 第一个参数
execute(): 执行预处理语句。insert_id: 获取刚刚插入数据的自增 ID。
读取数据
这是最常见的操作,用于从数据库中获取数据并显示在网页上。
文件:read_posts.php
<?php
require_once 'config.php';
// 1. 准备 SQL 查询语句
$sql = "SELECT id, title, content FROM posts ORDER BY id DESC"; // 按ID降序排列,最新的在最前面
// 2. 执行查询
$result = $conn->query($sql);
// 3. 检查查询结果
if ($result->num_rows > 0) {
// 输出数据
while($row = $result->fetch_assoc()) {
// fetch_assoc() 将结果集的一行作为关联数组返回
echo "<h2>" . htmlspecialchars($row["title"]) . "</h2>";
echo "<p>" . nl2br(htmlspecialchars($row["content"])) . "</p>";
echo "<hr>";
}
} else {
echo "0 结果";
}
// 4. 关闭连接
$conn->close();
?>
代码解释:
$conn->query(): 执行一个 SQL 查询。$result->num_rows: 返回结果集中的行数。$result->fetch_assoc(): 从结果集中获取一行作为关联数组。$row["title"]可以通过列名 "title" 来访问该列的值。htmlspecialchars(): 非常重要的安全函数! 将特殊字符转换为 HTML 实体,这可以防止跨站脚本攻击。nl2br(): 将换行符\n转换为 HTML 的<br>标签,这样在 HTML 中也能正确显示段落。
更新数据
更新数据与创建数据非常相似,都使用预处理语句。
文件:update_post.php
<?php
require_once 'config.php';
// 假设我们要更新 ID 为 1 的文章
$post_id = 1;
$new_title = "更新后的标题";
$new_content = "这篇文章的内容已经被更新了。";
// 1. 准备 SQL 语句
$sql = "UPDATE posts SET title = ?, content = ? WHERE id = ?";
// 2. 准备预处理语句
$stmt = $conn->prepare($sql);
// 3. 绑定参数 (注意类型: "ssi" 代表 字符串, 字符串, 整数)
$stmt->bind_param("ssi", $new_title, $new_content, $post_id);
// 4. 执行
if ($stmt->execute()) {
echo "记录更新成功!";
} else {
echo "Error updating record: " . $stmt->error;
}
$stmt->close();
$conn->close();
?>
删除数据
删除数据同样遵循预处理语句的模式。
文件:delete_post.php
<?php
require_once 'config.php';
// 假设我们要删除 ID 为 1 的文章
$post_id = 1;
// 1. 准备 SQL 语句
$sql = "DELETE FROM posts WHERE id = ?";
// 2. 准备预处理语句
$stmt = $conn->prepare($sql);
// 3. 绑定参数
$stmt->bind_param("i", $post_id);
// 4. 执行
if ($stmt->execute()) {
echo "记录删除成功!";
} else {
echo "Error deleting record: " . $stmt->error;
}
$stmt->close();
$conn->close();
?>
第五部分:安全第一 - 防止 SQL 注入
在前面的例子中,我们已经使用了预处理语句,这是防止 SQL 注入攻击最有效的方法。
什么是 SQL 注入? SQL 注入是一种攻击技术,攻击者通过在输入字段中输入恶意的 SQL 代码,来操纵后台的 SQL 查询。
不安全的例子 (绝对不要这样做!):
// 危险!不要直接拼接变量到 SQL 中! $user_input = "admin' -- "; // 攻击者输入的内容 $sql = "SELECT * FROM users WHERE username = '$user_input'"; // 最终的 SQL 变成了: SELECT * FROM users WHERE username = 'admin' -- ' // -- 是 SQL 的注释符,后面的内容被忽略了,这个查询可能会返回 admin 的信息,甚至绕过密码检查
为什么预处理语句更安全? 预处理语句将 SQL 命令和要发送的数据分离开,数据库引擎会先编译 SQL 命令,然后只将数据作为纯粹的值传递,即使数据中包含 SQL 代码,它也会被当作普通字符串处理,而不会被当作命令执行。
第六部分:完整示例 - 简易留言板
现在我们把所有知识整合起来,创建一个简单的留言板。
数据库准备
确保你的 my_blog 数据库中有一个 messages 表。
CREATE TABLE messages (
id INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
author VARCHAR(100) NOT NULL,
message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
文件结构
/my_guestbook
|-- config.php
|-- index.php (显示留言和表单)
|-- post_message.php (处理新留言提交)
config.php (与之前相同)
<?php
$db_host = 'localhost';
$db_user = 'root';
$db_pass = '';
$db_name = 'my_blog';
$conn = new mysqli($db_host, $db_user, $db_pass, $db_name);
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
$conn->set_charset("utf8mb4");
?>
index.php (主页面)
<?php
require_once 'config.php';
// 处理表单提交
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$author = $_POST['author'];
$message_text = $_POST['message'];
// 简单的验证
if (!empty($author) && !empty($message_text)) {
$sql = "INSERT INTO messages (author, message) VALUES (?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ss", $author, $message_text);
if ($stmt->execute()) {
// 提交成功,重定向到首页,避免刷新重复提交
header("Location: index.php");
exit();
} else {
echo "Error: " . $stmt->error;
}
$stmt->close();
}
}
// 读取所有留言
$sql = "SELECT author, message, created_at FROM messages ORDER BY created_at DESC";
$result = $conn->query($sql);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">简易留言板</title>
<style>
body { font-family: sans-serif; max-width: 800px; margin: 20px auto; padding: 0 20px; }
.message-box { border: 1px solid #ccc; padding: 15px; margin-bottom: 15px; border-radius: 5px; }
.message-box .author { font-weight: bold; color: #0056b3; }
.message-box .date { font-size: 0.8em; color: #6c757d; margin-bottom: 10px; }
.message-box .content { margin-top: 10px; }
form { margin-top: 30px; padding: 20px; border: 1px solid #ccc; border-radius: 5px; background-color: #f9f9f9; }
input[type="text"], textarea { width: 100%; padding: 8px; margin-bottom: 10px; border-radius: 3px; border: 1px solid #ccc; box-sizing: border-box; }
input[type="submit"] { background-color: #007bff; color: white; padding: 10px 15px; border: none; border-radius: 3px; cursor: pointer; }
</style>
</head>
<body>
<h1>我的留言板</h1>
<!-- 留言表单 -->
<form action="index.php" method="POST">
<label for="author">你的名字:</label>
<input type="text" id="author" name="author" required>
<label for="message">留言内容:</label>
<textarea id="message" name="message" rows="5" required></textarea>
<input type="submit" value="提交留言">
</form>
<hr>
<!-- 显示留言列表 -->
<h2>所有留言</h2>
<?php
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
echo "<div class='message-box'>";
echo "<div class='author'>" . htmlspecialchars($row["author"]) . "</div>";
echo "<div class='date'>" . $row["created_at"] . "</div>";
echo "<div class='content'>" . nl2br(htmlspecialchars($row["message"])) . "</div>";
echo "</div>";
}
} else {
echo "还没有留言,快来抢沙发吧!";
}
$conn->close();
?>
</body>
</html>
post_message.php (可选,但更规范)
你可以把 index.php 中处理提交的逻辑放到这个文件里,然后用 header("Location: index.php"); 重定向回来,这样可以更好地分离逻辑。
<?php
require_once 'config.php';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$author = $_POST['author'];
$message_text = $_POST['message'];
if (!empty($author) && !empty($message_text)) {
$sql = "INSERT INTO messages (author, message) VALUES (?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ss", $author, $message_text);
$stmt->execute();
$stmt->close();
}
}
// 无论成功与否,都重定向回首页
header("Location: index.php");
exit();
?>
然后将 index.php 中的 if ($_SERVER["REQUEST_METHOD"] == "POST") { ... } 整个块删除,并把表单的 action 改为 post_message.php。
总结与进阶
你已经掌握了 PHP + MySQL 的基础操作!
-
回顾要点:
- 使用 XAMPP/WAMP 搭建本地环境。
- 使用 phpMyAdmin 创建和管理数据库。
- 使用
new mysqli()建立数据库连接。 - **始终使用预处理语句 (
prepare,bind_param,execute) 来防止 SQL 注入。 - 使用
query()来执行查询(如SELECT)。 - 使用
fetch_assoc()来遍历查询结果。 - 使用
htmlspecialchars()来防止 XSS 攻击。 - 使用
header("Location: ...")进行页面重定向。
-
下一步学习方向:
- PDO (PHP Data Objects):另一种数据库抽象层,比 MySQLi 更通用,可以支持多种数据库(MySQL, PostgreSQL, SQLite 等)。
- MVC 模式:学习 Model-View-Controller 架构,这是一种更规范、更易于维护的代码组织方式。
- 框架:学习使用成熟的 PHP 框架,如 Laravel 或 Symfony,它们为你处理了大量的底层工作,让你能更专注于业务逻辑。
- RESTful API:学习如何使用 PHP 和 MySQL 构建 API,为移动应用或其他前端服务提供数据。
希望这份教程对你有帮助!祝你学习愉快!
