針對已載入記憶體的陣列進行分頁 (如 API 回傳全量資料、小型靜態清單),可使用 array_slice 快速切分。本文提供封裝函式、分頁資訊結構化輸出、Bootstrap 5 前端渲染範例、大陣列效能優化與 Generator 替代方案。
/**
* 陣列分頁
* @param array $items 完整資料陣列
* @param int $perPage 每頁筆數
* @param int $page 目前頁碼 (1-based)
* @param bool $preserveKeys 是否保留原鍵名
* @return array ['data' => [], 'pagination' => []]
*/
function paginateArray(array $items, int $perPage = 15, int $page = 1, bool $preserveKeys = false): array
{
$total = count($items);
$totalPages = max(1, (int)ceil($total / $perPage));
$page = max(1, min($page, $totalPages));
$offset = ($page - 1) * $perPage;
$data = array_slice($items, $offset, $perPage, $preserveKeys);
$pagination = [
'current_page' => $page,
'per_page' => $perPage,
'total' => $total,
'total_pages' => $totalPages,
'from' => $total ? $offset + 1 : 0,
'to' => min($offset + $perPage, $total),
'has_more' => $page < $totalPages,
'links' => [
'first' => 1,
'prev' => $page > 1 ? $page - 1 : null,
'next' => $page < $totalPages ? $page + 1 : null,
'last' => $totalPages,
],
];
return compact('data', 'pagination');
}
$allUsers = [
['id' => 1, 'name' => 'Alice'],
['id' => 2, 'name' => 'Bob'],
// ... 100 筆
];
$page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
$result = paginateArray($allUsers, 10, $page);
foreach ($result['data'] as $user) {
echo "{$user['name']}";
}
$p = $result['pagination'];
echo "";
function renderPagination(array $pagination, string $baseUrl = '?'): string
{
if ($pagination['total_pages'] <= 1) return '';
$html = '';
return $html;
}
// 使用
echo renderPagination($result['pagination'], '?page=');
| 資料量 | 建議方案 | 理由 |
|---|---|---|
| < 10,000 筆 | array_slice 直接切分 | 記憶體佔用極低,程式碼最簡 |
| 10,000 ~ 100,000 | 資料庫層面 LIMIT/OFFSET | 避免 PHP 載入全量陣列 |
| > 100,000 | 游標分頁 / Keyset Pagination | WHERE id > ? ORDER BY id LIMIT ? 避免深分頁效能劣化 |
| 串流/無限資料 | Generator 分塊輸出 | 不佔用全量記憶體 |
function paginateGenerator(iterable $source, int $perPage, int $page): array
{
$offset = ($page - 1) * $perPage;
$data = [];
$count = 0;
$total = 0;
foreach ($source as $item) {
$total++;
if ($count >= $offset && count($data) < $perPage) {
$data[] = $item;
}
$count++;
}
return [
'data' => $data,
'pagination' => [
'current_page' => $page,
'per_page' => $perPage,
'total' => $total,
'total_pages' => max(1, (int)ceil($total / $perPage)),
],
];
}
// 使用:資料來源可以是 Generator、資料庫游標、API 分頁迭代器
$source = function() {
$stmt = $pdo->query("SELECT * FROM huge_table");
while ($row = $stmt->fetch()) yield $row;
};
$result = paginateGenerator($source(), 20, $page);
array_slice 第四參數 true 保留鍵名,但分頁邏輯通常需重新索引 (預設 false)。max(1, min($page, $totalPages)) 邊界修正,導致負偏移或空頁。http_build_query(array_merge($_GET, ['page' => $p]))。陣列分頁適合「資料量小、已在記憶體、需快速原型」場景。生產環境核心業務請務必下推到資料庫層 (LIMIT/OFFSET 或 Keyset Pagination),避免記憶體暴增與網路傳輸浪費。