上一部分我们学习了函数,学会了把重复的代码打包复用。但当代码越来越多,一个文件里塞几百上千行时,你会发现问题来了:找个函数要翻半天,改一个小地方可能要同时改好几个文件。
文件包含就是解决这个问题的:把不同功能的代码拆分成多个文件,然后在需要的地方引入。
这一部分还会讲到表单处理(接收用户提交的数据)和数据库基础(让数据持久保存)。这三块内容合在一起,你就能做出一个完整的用户注册、保存、展示的系统了。
---
一、文件包含:把代码拆成多个文件
1.1 为什么需要文件包含
假设你写了一个网站,有20个页面。每个页面都需要判断用户是否登录。如果没有文件包含,你需要在20个文件里各写一遍登录判断代码——改一次就要改20个地方,非常容易出错。
正确的做法:把登录判断写在一个单独的文件里,然后在20个页面中都引入这个文件。
1.2 四种包含语句
PHP提供了四种文件包含语句,区别在于找不到文件时的反应和是否重复包含:
语句 文件不存在时 是否可重复包含
include 警告,继续执行 是
include_once 警告,继续执行 否
require 致命错误,停止执行 是
require_once 致命错误,停止执行 否
使用建议:
· 核心配置文件、函数库 → 用 require_once
· 非必须的辅助模块(如广告位) → 用 include
· _once 后缀能防止同一个文件被重复包含导致的错误(比如重复定义函数)
1.3 基础用法
创建被包含的文件: functions.php
```php
<?php
// 公共函数库
function greet($name) {
return "你好," . $name;
}
$siteName = "我的PHP网站";
?>
```
在主文件中包含: index.php
```php
<?php
// 引入函数库
require_once "functions.php";
// 现在可以直接使用 functions.php 里的函数和变量
echo greet("张三"); // 输出:你好,张三
echo $siteName; // 输出:我的PHP网站
?>
```
1.4 实际项目结构示例
一个简单项目的目录结构:
```
project/
├── config/
│ └── database.php # 数据库配置
├── functions/
│ └── common.php # 公共函数
├── includes/
│ ├── header.php # 页面头部
│ └── footer.php # 页面底部
├── index.php # 主页面
└── register.php # 注册页面
```
header.php:
```php
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><?php echo $pageTitle ?? "默认标题"; ?></title>
</head>
<body>
<div class="header">网站公共头部</div>
```
footer.php:
```php
<div class="footer">网站公共底部</div>
</body>
</html>
```
index.php:
```php
<?php
$pageTitle = "首页";
require_once "includes/header.php";
?>
<div class="content">
<h1>欢迎访问我的网站</h1>
<p>这里是首页内容</p>
</div>
<?php require_once "includes/footer.php"; ?>
```
这种“头部-内容-底部”的拆分方式,是PHP开发中最常见的页面组织模式。
1.5 路径问题
包含文件时,路径写不对是新手最常见的错误。
· 当前目录:include "header.php"
· 上级目录:include "../config/database.php"
· 绝对路径(推荐,更可靠):include $_SERVER['DOCUMENT_ROOT'] . "/includes/header.php"
---
二、表单处理:接收用户数据
PHP最核心的用途之一就是处理HTML表单提交的数据。
2.1 GET 与 POST 的区别
表单数据可以通过两种方式提交:
方法 数据位置 特点 适用场景
GET URL中(如 ?name=张三&age=18) 有长度限制,数据可见,可添加书签 搜索、筛选、翻页
POST HTTP请求体中 无长度限制,数据不可见 登录、注册、提交订单
PHP 中分别用 $_GET 和 $_POST 数组接收:
```php
$name = $_GET["name"]; // 接收GET方式提交的数据
$name = $_POST["name"]; // 接收POST方式提交的数据
```
如果不确定是GET还是POST,可以用 $_REQUEST(包含两者,但不推荐,不够明确)。
2.2 一个完整的表单示例
register.html(表单页面):
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户注册</title>
</head>
<body>
<form action="register.php" method="post">
<label>用户名:</label>
<input type="text" name="username" required><br><br>
<label>密码:</label>
<input type="password" name="password"><br><br>
<label>性别:</label>
<input type="radio" name="gender" value="男"> 男
<input type="radio" name="gender" value="女"> 女<br><br>
<label>爱好:</label>
<input type="checkbox" name="hobby[]" value="阅读"> 阅读
<input type="checkbox" name="hobby[]" value="运动"> 运动
<input type="checkbox" name="hobby[]" value="音乐"> 音乐<br><br>
<label>城市:</label>
<select name="city">
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
</select><br><br>
<label>个人简介:</label><br>
<textarea name="bio" rows="5" cols="40"></textarea><br><br>
<input type="submit" value="注册">
</form>
</body>
</html>
```
register.php(处理页面):
```php
<?php
// 检查请求方法
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// 接收文本输入
$username = $_POST["username"] ?? "";
$password = $_POST["password"] ?? "";
$gender = $_POST["gender"] ?? "";
$city = $_POST["city"] ?? "";
$bio = $_POST["bio"] ?? "";
// 接收复选框(返回数组)
$hobbies = $_POST["hobby"] ?? [];
// 简单验证
$errors = [];
if (empty($username)) {
$errors[] = "用户名不能为空";
} elseif (strlen($username) < 3) {
$errors[] = "用户名至少需要3个字符";
}
if (empty($password)) {
$errors[] = "密码不能为空";
} elseif (strlen($password) < 6) {
$errors[] = "密码至少需要6个字符";
}
// 如果有错误,显示错误信息
if (!empty($errors)) {
echo "<h3>提交失败:</h3><ul>";
foreach ($errors as $error) {
echo "<li>" . $error . "</li>";
}
echo "</ul><a href='register.html'>返回重新填写</a>";
exit;
}
// 处理成功,显示提交的数据
echo "<h3>注册成功!</h3>";
echo "用户名:" . htmlspecialchars($username) . "<br>";
echo "性别:" . htmlspecialchars($gender) . "<br>";
echo "城市:" . htmlspecialchars($city) . "<br>";
echo "爱好:" . implode("、", $hobbies) . "<br>";
echo "个人简介:" . nl2br(htmlspecialchars($bio)) . "<br>";
}
?>
```
2.3 安全提醒:永远不要信任用户输入
上面的代码中用了 htmlspecialchars(),这个函数很重要。它的作用是把 HTML 特殊字符转换成安全的形式:
原始字符 转换后
< <
> >
" "
' '
如果不做这个转换,用户可以在输入框中提交 <script>alert("攻击")</script> 之类的代码,你的页面就会执行它。这叫XSS攻击(跨站脚本攻击)。
记住:任何输出到HTML中的用户数据,都要用 htmlspecialchars() 处理。
---
三、数据库基础(MySQL)
表单数据目前只是显示一下就没了,刷新页面就丢失。要想永久保存用户数据,就需要数据库。
MySQL 是最常用的与 PHP 搭配的数据库,免费、稳定、性能好。
3.1 连接数据库
首先需要一个配置文件 config/database.php:
```php
<?php
// 数据库配置信息
$db_host = "localhost"; // 数据库服务器地址
$db_user = "root"; // 数据库用户名
$db_password = ""; // 数据库密码(本地环境通常为空)
$db_name = "test_db"; // 数据库名
// 创建连接
$conn = new mysqli($db_host, $db_user, $db_password, $db_name);
// 检查连接是否成功
if ($conn->connect_error) {
die("连接失败:" . $conn->connect_error);
}
// 设置字符集,防止中文乱码
$conn->set_charset("utf8mb4");
?>
```
3.2 创建数据表
在连接数据库之后,需要创建一张表来存放用户数据。可以在 phpMyAdmin 中执行,也可以用 SQL 语句:
```sql
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
gender VARCHAR(10),
city VARCHAR(50),
bio TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
字段说明:
字段 类型 说明
id INT 自动递增的唯一编号
username VARCHAR(50) 用户名,不能为空,不能重复
password VARCHAR(255) 密码(实际应用中需加密存储)
gender VARCHAR(10) 性别
city VARCHAR(50) 城市
bio TEXT 个人简介(长文本)
created_at TIMESTAMP 注册时间,自动生成
3.3 插入数据(INSERT)
把表单接收的数据保存到数据库:
```php
<?php
require_once "config/database.php";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = $_POST["username"] ?? "";
$password = $_POST["password"] ?? "";
$gender = $_POST["gender"] ?? "";
$city = $_POST["city"] ?? "";
$bio = $_POST["bio"] ?? "";
// 验证代码略(参考上一节)
// 重要:密码绝对不能明文存储!这里先用md5演示,实际应用应使用 password_hash()
$hashedPassword = md5($password);
// 准备SQL语句(使用预处理语句防止SQL注入)
$sql = "INSERT INTO users (username, password, gender, city, bio) VALUES (?, ?, ?, ?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("sssss", $username, $hashedPassword, $gender, $city, $bio);
if ($stmt->execute()) {
echo "注册成功!用户ID是:" . $conn->insert_id;
} else {
if ($conn->errno == 1062) { // 重复键错误
echo "用户名已被占用,请换一个";
} else {
echo "注册失败:" . $stmt->error;
}
}
$stmt->close();
$conn->close();
}
?>
```
3.4 查询数据(SELECT)
从数据库中读取用户数据并展示:
```php
<?php
require_once "config/database.php";
// 查询所有用户
$sql = "SELECT id, username, gender, city, created_at FROM users ORDER BY id DESC";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
echo "<table border='1' cellpadding='8'>";
echo "<tr><th>ID</th><th>用户名</th><th>性别</th><th>城市</th><th>注册时间</th></tr>";
while ($row = $result->fetch_assoc()) {
echo "<tr>";
echo "<td>" . $row["id"] . "</td>";
echo "<td>" . htmlspecialchars($row["username"]) . "</td>";
echo "<td>" . htmlspecialchars($row["gender"]) . "</td>";
echo "<td>" . htmlspecialchars($row["city"]) . "</td>";
echo "<td>" . $row["created_at"] . "</td>";
echo "</tr>";
}
echo "</table>";
} else {
echo "暂无用户数据";
}
$conn->close();
?>
```
3.5 SQL注入与预处理语句
上面代码中使用了预处理语句,这是必须养成的习惯。
什么是SQL注入?
假设你直接拼接SQL语句:
```php
// 危险写法!千万不要这样写
$sql = "SELECT * FROM users WHERE username = '" . $username . "'";
```
如果用户输入的 $username 是 ' OR '1'='1,拼接后的SQL变成:
```sql
SELECT * FROM users WHERE username = '' OR '1'='1'
```
这个条件永远为真,攻击者可以绕过登录验证,甚至删除数据。
预处理语句的原理:SQL语句的结构和数据是分开发送的,数据库知道哪里是数据,不会把数据当成SQL命令执行。
```php
// 安全的写法
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
$stmt->bind_param("s", $username); // "s" 表示参数是字符串
$stmt->execute();
```
记住:任何用户输入的数据,在SQL语句中使用时,必须用预处理语句或转义函数处理。
---
四、综合练习:完整的用户注册系统
把上面所有内容整合起来,实现一个完整的用户注册、展示系统。
项目结构:
```
register_system/
├── config/
│ └── database.php # 数据库配置
├── includes/
│ ├── header.php # 页面头部
│ └── footer.php # 页面底部
├── index.php # 用户列表页
├── register.php # 注册处理页
└── register_form.php # 注册表单页
```
register_form.php:
```php
<?php $pageTitle = "用户注册"; ?>
<?php require_once "includes/header.php"; ?>
<h2>用户注册</h2>
<form action="register.php" method="post">
<label>用户名:</label>
<input type="text" name="username" required minlength="3"><br><br>
<label>密码:</label>
<input type="password" name="password" required minlength="6"><br><br>
<label>性别:</label>
<input type="radio" name="gender" value="男"> 男
<input type="radio" name="gender" value="女"> 女<br><br>
<label>城市:</label>
<select name="city">
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
</select><br><br>
<label>个人简介:</label><br>
<textarea name="bio" rows="5" cols="40"></textarea><br><br>
<input type="submit" value="注册">
</form>
<?php require_once "includes/footer.php"; ?>
```
register.php:
```php
<?php
require_once "config/database.php";
if ($_SERVER["REQUEST_METHOD"] != "POST") {
header("Location: register_form.php");
exit;
}
$username = trim($_POST["username"] ?? "");
$password = $_POST["password"] ?? "";
$gender = $_POST["gender"] ?? "";
$city = $_POST["city"] ?? "";
$bio = trim($_POST["bio"] ?? "");
// 验证
$errors = [];
if (strlen($username) < 3) $errors[] = "用户名至少3个字符";
if (strlen($password) < 6) $errors[] = "密码至少6个字符";
if (!in_array($gender, ["男", "女"])) $errors[] = "请选择性别";
if (!empty($errors)) {
echo "<h3>提交失败:</h3><ul>";
foreach ($errors as $error) echo "<li>$error</li>";
echo "</ul><a href='register_form.php'>返回</a>";
exit;
}
// 密码加密(实际应用请使用 password_hash())
$hashedPassword = md5($password);
// 插入数据库
$sql = "INSERT INTO users (username, password, gender, city, bio) VALUES (?, ?, ?, ?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("sssss", $username, $hashedPassword, $gender, $city, $bio);
if ($stmt->execute()) {
echo "<h3>注册成功!</h3>";
echo "<p>欢迎 " . htmlspecialchars($username) . " 加入</p>";
echo "<a href='index.php'>查看用户列表</a>";
} else {
if ($conn->errno == 1062) {
echo "用户名已被占用,请<a href='register_form.php'>重新注册</a>";
} else {
echo "注册失败:" . $stmt->error;
}
}
$stmt->close();
$conn->close();
?>
```
index.php(用户列表):
```php
<?php
$pageTitle = "用户列表";
require_once "includes/header.php";
require_once "config/database.php";
$sql = "SELECT id, username, gender, city, created_at FROM users ORDER BY id DESC";
$result = $conn->query($sql);
?>
<h2>用户列表</h2>
<?php if ($result->num_rows > 0): ?>
<table border="1" cellpadding="8" cellspacing="0">
<tr>
<th>ID</th>
<th>用户名</th>
<th>性别</th>
<th>城市</th>
<th>注册时间</th>
</tr>
<?php while ($row = $result->fetch_assoc()): ?>
<tr>
<td><?php echo $row["id"]; ?></td>
<td><?php echo htmlspecialchars($row["username"]); ?></td>
<td><?php echo htmlspecialchars($row["gender"]); ?></td>
<td><?php echo htmlspecialchars($row["city"]); ?></td>
<td><?php echo $row["created_at"]; ?></td>
</tr>
<?php endwhile; ?>
</table>
<?php else: ?>
<p>暂无用户,<a href="register_form.php">去注册</a></p>
<?php endif; ?>
<?php
$conn->close();
require_once "includes/footer.php";
?>
```
---
五、常见错误与避坑指南
错误现象 常见原因 解决方法
包含文件时报错 路径不对 使用绝对路径或确认相对路径层级
表单提交后收不到数据 method 与接收方式不匹配 POST 表单用 $_POST,GET 表单用 $_GET
页面上显示了原始 HTML 标签 忘记用 htmlspecialchars() 输出用户数据前调用该函数
中文存入数据库变成问号 字符集设置错误 连接后执行 set_charset("utf8mb4")
SQL 执行成功但数据没变化 忘记提交事务(InnoDB) 非自动提交模式下需要 commit()
密码存储为明文 安全意识不足 使用 password_hash() 和 password_verify()
---
六、小结
这一部分我们学习了三个核心内容:
知识点 核心内容
文件包含 include / require / _once 变体,拆分代码文件
表单处理 $_GET / $_POST 接收数据,htmlspecialchars() 防XSS
数据库操作 MySQLi 连接、预处理语句防SQL注入、增删改查
安全原则 永远不信任用户输入,输出时转义,存储时加密
学完这一部分,你已经能做出一个完整的用户注册和展示系统了。这也是很多PHP初学者迈过的第一道坎——从“显示信息”到“保存信息”的跨越。
下一部分(也是本教程的最后一部分),我们将学习会话管理(Session与Cookie),实现用户登录、退出、记住登录状态等功能——让你的网站不再是“谁都能访问”,而是能区分不同用户。