面對成千上萬筆資料,一次性載入會導致記憶體溢出、回應緩慢。分頁器將資料切割為固定大小的頁面,配合 LIMIT 查詢,是後端開發必備技能。本文提供一個功能完整、風格類似 Google/Baidu 的 PHP 分頁類 Pager,支援自訂頁碼範圍、首尾頁、上下頁、跳轉等功能。
<?php
/**
* Pager 分頁類
* 作者:潇湘博客
* 日期:2009-11-26
* 用法:見文末範例
*/
class Pager {
// 屬性
protected $pageTotal = 0; // 總頁數
protected $previous = 0; // 上一頁
protected $next = 0; // 下一頁
protected $startPage = 0; // 中間頁碼起始
protected $endPage = 0; // 中間頁碼結束
protected $recorbTotal = 0; // 總記錄數
protected $pageSize = 20; // 每頁筆數
protected $currentPage = 1; // 當前頁
protected $baseUri = ''; // 基礎 URL
// Getter / Setter
public function getBaseUri() { return $this->baseUri; }
public function setBaseUri($uri) { $this->baseUri = $uri; }
public function getCurrentPage() { return $this->currentPage; }
public function setCurrentPage($page) { $this->currentPage = max(1, intval($page)); }
public function getPageSize() { return $this->pageSize; }
public function setPageSize($size) { $this->pageSize = max(1, intval($size)); }
public function getRecorbTotal() { return $this->recorbTotal; }
public function setRecorbTotal($total) { $this->recorbTotal = max(0, intval($total)); }
public function __construct() {
$this->pageSize = 20;
$this->currentPage = 1;
}
// 核心演算法
private function arithmetic() {
if ($this->currentPage < 1) $this->currentPage = 1;
// 計算總頁數
$this->pageTotal = floor($this->recorbTotal / $this->pageSize) +
($this->recorbTotal % $this->pageSize > 0 ? 1 : 0);
// 當前頁超出範圍,導向最後一頁
if ($this->currentPage > $this->pageTotal && $this->pageTotal > 0) {
header('Location: ' . $this->baseUri . 'page=' . $this->pageTotal);
exit;
}
$this->next = $this->currentPage + 1;
$this->previous = $this->currentPage - 1;
// 計算顯示的頁碼範圍(以當前頁為中心,前後各 5 頁,共 11 頁)
$this->startPage = ($this->currentPage + 5) > $this->pageTotal
? $this->pageTotal - 10
: $this->currentPage - 5;
$this->endPage = $this->currentPage < 5 ? 11 : $this->currentPage + 5;
if ($this->startPage < 1) $this->startPage = 1;
if ($this->pageTotal < $this->endPage) $this->endPage = $this->pageTotal;
}
// 輸出 HTML 樣式
protected function pageStyle() {
$result = "共 {$this->pageTotal} 頁 ";
// 首頁、上一頁
if ($this->currentPage > 1) {
$result .= '<a href="' . $this->baseUri . 'page=1">首頁</a> ';
$result .= '<a href="' . $this->baseUri . 'page=' . $this->previous . '">上一頁</a> ';
} else {
$result .= '首頁 上一頁 ';
}
// 中間頁碼
for ($i = $this->startPage; $i <= $this->endPage; $i++) {
if ($this->currentPage == $i) {
$result .= '<font color="#ff0000">' . $i . '</font> ';
} else {
$result .= '<a href="' . $this->baseUri . 'page=' . $i . '">' . $i . '</a> ';
}
}
// 下一頁、尾頁
if ($this->currentPage != $this->pageTotal) {
$result .= '<a href="' . $this->baseUri . 'page=' . $this->next . '">下一頁</a> ';
$result .= '<a href="' . $this->baseUri . 'page=' . $this->pageTotal . '">尾頁</a>';
} else {
$result .= '下一頁 尾頁';
}
return $result;
}
// 執行輸出
public function execute() {
if ($this->baseUri === '' || $this->recorbTotal == 0) return '';
$this->arithmetic();
return $this->pageStyle();
}
}
?>
<?php
require_once 'Pager.class.php';
$pager = new Pager();
// 從 URL 取得當前頁,預設第 1 頁
$pager->setCurrentPage(isset($_GET['page']) ? $_GET['page'] : 1);
// 設定總記錄數(實際專案從資料庫 COUNT(*) 取得)
$pager->setRecorbTotal(1000);
// 設定基礎 URL(注意結尾需包含 ? 或 &)
$pager->setBaseUri("list.php?");
// 輸出分頁 HTML
echo $pager->execute();
?>
<?php
// 1. 連線資料庫
$pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8", "root", "root");
// 2. 取得總筆數
$stmt = $pdo->query("SELECT COUNT(*) FROM article");
$total = $stmt->fetchColumn();
// 3. 初始化分頁器
$pager = new Pager();
$pager->setRecorbTotal($total);
$pager->setPageSize(15);
$pager->setCurrentPage(isset($_GET['page']) ? $_GET['page'] : 1);
$pager->setBaseUri("article_list.php?");
// 4. 計算 LIMIT 參數
$offset = ($pager->getCurrentPage() - 1) * $pager->getPageSize();
$limit = $pager->getPageSize();
// 5. 查詢當頁資料
$sql = "SELECT id, title, created_at FROM article ORDER BY id DESC LIMIT {$offset}, {$limit}";
$rows = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
// 6. 輸出資料表格
foreach ($rows as $row) {
echo "<div>{$row['title']} - {$row['created_at']}</div>";
}
// 7. 輸出分頁導航
echo $pager->execute();
?>
pageStyle() 回傳陣列/物件,由前端模板渲染,支援 Bootstrap、Tailwind 等框架。/page/3/),需修改 baseUri 組裝邏輯。ajaxTarget 參數,輸出 data-page 屬性,配合 JS 無刷新載入。COUNT(*) 可快取 60 秒或使用近似值。| 問題 | 原因與解決 |
|---|---|
| 點擊頁碼跳轉錯誤 | setBaseUri 結尾缺少 ? 或 &,導致參數串聯錯誤 |
| 總頁數為 0 仍顯示分頁 | 執行 execute() 前檢查 recorbTotal > 0 |
| 頁碼範圍計算異常 | 總頁數較小時(< 11),startPage/endPage 邏輯需處理邊界 |
| 中文參數亂碼 | URL 產生時使用 urlencode(),接收端 urldecode() |
分頁器看似簡單,但邊界條件(首尾頁、單頁、大數據量)處理不當極易出錯。封裝成可複用的 Pager 類,配合單元測試,能大幅提升開發效率與程式穩定性。現代專案建議搭配前端框架(Vue/React)實作 AJAX 分頁,後端僅提供 JSON API。