PHP 网站分页功能怎么实现?MySQL 查询和前端页码应该如何配合
图片说明:示意图 图片来源:Public Domain(公有领域)
当网站文章从几十条增长到几万条,直接把所有记录一次性查询并显示在页面上,不仅会增加数据库和服务器的负担,也会拖慢页面加载速度。分页功能通过限制每次查询返回的记录数量,让数据库、PHP 后端和前端页码控件协同工作。对于使用 PHP、PDO 和 MySQL 的网站,关键是正确计算查询偏移量、保持记录排序稳定、验证用户输入,并根据数据规模选择合适的分页方式。
一、MySQL 如何查询指定页的数据
MySQL 通常使用 LIMIT 和 OFFSET 实现传统分页。假设每页显示10条记录,第1页从第0条偏移量开始,第2页跳过前10条,第3页跳过前20条。查询第2页的 SQL 可以写成:
SELECT id, title, created_at
FROM articles
ORDER BY created_at DESC, id DESC
LIMIT 10 OFFSET 10;
这里的 LIMIT 10 表示最多返回10条记录,OFFSET 10 表示跳过前10条。ORDER BY created_at DESC, id DESC 则保证结果按发布时间倒序排列;当多篇文章的发布时间相同时,再使用唯一的 id 决定先后顺序。
如果没有明确排序,数据库不保证每次都以相同顺序返回记录,用户翻页时可能看到重复文章或遗漏文章。因此,实际网站不应只写 SELECT * FROM articles LIMIT 10 OFFSET 10,而应根据业务需要选择字段,并设置稳定的排序条件。
二、如何计算总页数
传统分页一般需要先查询符合条件的记录总数,再计算总页数。假设总记录数为256条,每页显示10条,那么总页数就是26页。PHP 可以使用 ceil() 向上取整:
$totalPages = (int) ceil($total / $limit);
其中 $total 是符合筛选条件的记录数量,$limit 是每页记录数。如果总记录数为0,总页数也为0,页面可以显示“暂无数据”,而不必生成无效的第1页链接。
查询总数时必须与实际分页查询使用相同的筛选条件。例如,文章列表只显示已发布文章,那么总数查询也应使用 WHERE publish_status = 1。如果总数统计全部文章,而分页查询只显示已发布文章,页码就可能过多,最后几页也可能没有记录。
对于复杂筛选、关联查询或数据量很大的表,应使用 EXPLAIN 检查查询计划,并评估索引和统计查询的成本。COUNT(*) 并非在所有情况下都很慢,但频繁执行、筛选条件复杂或需要扫描大量数据时,确实可能成为负担。
三、PHP 如何安全处理页码参数
用户可以直接修改网址中的 page 参数,因此不能把它当作可信输入。例如,?page=-1、?page=abc 和 ?page=100000000 都需要合理处理。
下面是传统分页中常用的参数处理方式:
$pageInput = $_GET['page'] ?? '1';
if (
!is_string($pageInput) ||
!preg_match('/^[1-9][0-9]*$/', $pageInput)
) {
$page = 1;
} else {
$page = (int) $pageInput;
}
$limit = 10;
这段代码只接受由正整数组成的页码字符串,其他输入统一回到第1页。实际项目还应考虑整数溢出或设置允许的最大页码,避免用户传入极端数值。
在完成总数查询后,再根据总页数修正页码:
$totalPages = (int) ceil($total / $limit);
if ($totalPages === 0) {
$page = 1;
} else {
$page = min($page, $totalPages);
}
$offset = ($page - 1) * $limit;
这里将超出范围的页码限制在最后一页。如果总记录数为0,则不会产生负数偏移量。需要注意,页码验证与 SQL 注入防护是两回事:参数验证用于控制业务范围,预处理语句则用于安全地传递查询参数。
四、完整的 PDO 分页查询示例
下面以文章表 articles 为例,展示统计总数、计算页码和查询当前页记录的基本流程。假设 $pdo 已经是配置好的 PDO 数据库连接,文章表包含 id、title、created_at 和 publish_status 字段。
query($countSql);
$total = (int) $totalStmt->fetchColumn();
// 计算总页数
$totalPages = (int) ceil($total / $limit);
// 修正超出范围的页码
if ($totalPages === 0) {
$page = 1;
} else {
$page = min($page, $totalPages);
}
// 计算偏移量
$offset = ($page - 1) * $limit;
// 查询当前页
$sql = "
SELECT id, title, created_at
FROM articles
WHERE publish_status = 1
ORDER BY created_at DESC, id DESC
LIMIT :limit OFFSET :offset
";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 输出文章
foreach ($articles as $article) {
echo '
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP