php 8.5 分页核心是 sql limit 与页码逻辑,需严格校验 page/per_page 类型、绑定整型参数防注入,并确保总数与分页查询条件一致,同时返回标准 http 头和 json 响应。

PHP 8.5 里用 mysqli 或 PDO 做分页,核心就两步
不是写个循环就能分页,关键在 SQL 的 LIMIT 和 PHP 控制页码逻辑。PHP 8.5 本身没新增分页函数,但对类型声明、错误处理更严格,稍不注意就会报 TypeError 或空结果。
你得自己算偏移量:offset = (page - 1) * per_page,再拼进查询。别信“自动分页类”,底层全是这行逻辑。
-
page必须是整数且 ≥ 1,否则跳转到第一页或直接拒掉(别用intval()粗暴转,它把"1abc"变成1) -
per_page建议硬编码或白名单限制(比如只允许10、20、50),防止有人传999999拖垮数据库 - 查总数时别用
SELECT COUNT(*) FROM (your_query)套子查询——MySQL 8.0+ 虽支持,但没加索引时比SELECT COUNT(*) FROM table WHERE ...慢得多
MySQL LIMIT 在 PHP 8.5 里怎么安全传参
直接字符串拼接 "LIMIT $offset, $limit" 是高危操作,PHP 8.5 的 mysqli 和 PDO 都要求显式绑定参数,否则会触发严格模式报错或 SQL 注入。
用 PDO 最省心:它原生支持 LIMIT 的占位符,但注意——? 占位符不能用于 LIMIT 的位置参数(MySQL 不认),必须用 bindValue 显式设为 PDO::PARAM_INT。
部分功能简介:商品收藏夹功能热门商品最新商品分级价格功能自选风格打印结算页面内部短信箱商品评论增加上一商品,下一商品功能增强商家提示功能友情链接用户在线统计用户来访统计用户来访信息用户积分功能广告设置用户组分类邮件系统后台实现更新用户数据系统图片设置模板管理CSS风格管理申诉内容过滤功能用户注册过滤特征字符IP库管理及来访限制及管理压缩,恢复,备份数据库功能上传文件管理商品类别管理商品添加/修改/
立即学习“PHP免费学习笔记(深入)”;
$pdo->prepare("SELECT * FROM posts WHERE status = ? ORDER BY id DESC LIMIT ?, ?");
$stmt->bindValue(1, 'published', PDO::PARAM_STR);
$stmt->bindValue(2, $offset, PDO::PARAM_INT);
$stmt->bindValue(3, $per_page, PDO::PARAM_INT);-
mysqli用户得用mysqli_stmt_bind_param("sii", $status, $offset, $per_page),类型字符必须严格匹配(i是 int,s是 string) - 别把
$offset当字符串 bind,否则 MySQL 报Invalid argument supplied for mysqli_stmt_bind_param() - PHP 8.5 默认开启
strict_types=1,如果函数参数声明了int,传浮点数(比如floor($page * $per_page)返回 float)会直接 Fatal Error
为什么分页总少一条或多一条?检查这几个地方
不是数据丢了,大概率是总数统计和分页查询的 WHERE 条件不一致。比如查列表用 WHERE status = 'published',但算总数时漏了这个条件,或者用了 WHERE status IN ('published', 'draft')。
- 总数 SQL 必须和分页 SQL 的
WHERE、JOIN、GROUP BY完全一致,只把字段换成COUNT(*) - 用
SQL_CALC_FOUND_ROWS?别用。MySQL 8.0.17+ 已废弃,PHP 8.5 配合新驱动可能返回错误结果 - 前端传
page=0或负数时,PHP 8.5 的abs((int)$_GET['page'])会变成0,导致LIMIT 0, 10查出第一页——但用户本意可能是出错,该返回 404 或重定向 - 用
DateTime排序分页时,如果有相同时间戳的多条记录,ORDER BY created_at DESC不稳定,翻页可能重复或跳过——加个二级排序,比如ORDER BY created_at DESC, id DESC
PHP 8.5 分页响应里最容易被忽略的 HTTP 头
光输出 JSON 不够,客户端(尤其是前端分页组件)需要知道总页数、当前页、每页几条。PHP 8.5 对数组键名大小写更敏感,['total' => 123] 和 ['Total' => 123] 是两个东西。
建议统一用小写键,同时加 X-Total-Count 头,很多 Vue/React 分页库(如 vue-tables-2)默认读这个头。
header('X-Total-Count: ' . $total);
header('Content-Type: application/json');
echo json_encode([
'data' => $rows,
'page' => $page,
'per_page' => $per_page,
'total' => $total,
'last_page' => (int)ceil($total / $per_page)
]);- 别在
json_encode()前输出任何空格或echo "",PHP 8.5 的headers_sent()检查更严,会直接报Warning: Cannot modify header information -
$total是整数,但万一数据库返回NULL(比如 WHERE 条件全不匹配),ceil(NULL / 10)在 PHP 8.5 会警告并返回FALSE,得先is_int($total) || $total = 0 - 如果用 Swoole 或 RoadRunner,
header()可能无效,得改用响应对象的withHeader()方法










