php返回json分页数据需手动构造结构,包括data列表和meta分页元信息,严格校验参数、正确计算offset、查询总数而非仅count($data),并设置content-type头。

PHP 返回 JSON 分页数据时,必须手动构造分页结构
PHP 本身没有内置的「JSON 分页」函数,json_encode() 只负责序列化,分页逻辑(如当前页、总条数、每页数量、数据列表)得你自己组织。常见错误是只返回 $data 数组,前端拿不到 total 或 page,导致无法渲染分页控件。
推荐结构:始终用统一顶层键包裹,比如 data 放列表、meta 放分页元信息。不要把分页字段和业务字段混在同级:
{
"data": [...],
"meta": {
"total": 127,
"per_page": 10,
"current_page": 3,
"last_page": 13,
"from": 21,
"to": 30
}
}- 后端计算
$total = $pdo->query("SELECT COUNT(*) FROM ...")->fetchColumn(),不能靠count($data)—— 那只是当前页数量 -
last_page要用ceil($total / $per_page)算,别四舍五入 - 前端传来的
page和per_page必须校验:max(1, (int)$_GET['page']),防 SQL 注入或越界
MySQL LIMIT 偏移量计算容易出错
分页的核心是 SQL 的 LIMIT offset, length,但 offset = (page - 1) * per_page 这个公式,很多人漏了减 1,结果第一页就跳过前 per_page 条。
示例(PDO):
立即学习“PHP免费学习笔记(深入)”;
$page = max(1, (int)($_GET['page'] ?? 1));
$per_page = max(1, min(100, (int)($_GET['per_page'] ?? 10))); // 限制最大每页100条
$offset = ($page - 1) * $per_page;
<p>$stmt = $pdo->prepare("SELECT id, title FROM posts WHERE status = ? ORDER BY id DESC LIMIT ?, ?");
$stmt->execute([1, $offset, $per_page]);
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);- 别用
LIMIT $page, $per_page—— 这是第$page页起始位置,不是「第几页」 - 大偏移量(如 page=10000)性能差,考虑游标分页(用上一页最后 ID)替代
- ORDER BY 字段必须有索引,否则
LIMIT+OFFSET会全表扫描
header('Content-Type: application/json') 不能少
不设 header,浏览器或前端框架(如 axios)可能把响应当文本处理,response.data 解析失败,控制台报 Unexpected token。
- 必须在
echo json_encode(...)前调用:header('Content-Type: application/json; charset=utf-8'); - 如果用了输出缓冲(ob_start),确保没意外输出空格或 BOM —— 会导致 JSON 解析失败,错误信息是
JSON parse error: Unexpected token - 开发时可用
var_dump(json_last_error_msg());检查编码失败原因,常见于中文未 UTF-8 编码或资源含 null 字节
Laravel 或 ThinkPHP 等框架已封装,但底层逻辑一样
框架的 paginate() 方法(如 Laravel 的 Post::paginate(10))返回的是 Paginator 对象,调用 ->toJson() 才是标准 JSON。它内部也做三件事:查总数、算 offset、查数据列表。
如果你在裸 PHP 里想模仿,关键就是别绕开「总数查询」—— 很多人图省事只查数据再 count($data),那 total 永远等于 per_page,前端永远显示「共 10 条」。
真正难的不是拼 JSON,是让分页参数、SQL 偏移、总数统计三者对齐。少一个环节,前端翻页就错乱。











