分頁是 Web 應用高頻功能。本文提供一個生產級 PHP 分頁類,支援靜態化偽靜態 URL(如 /list-p1.html)、AJAX 無刷新分頁、自定義顯示頁碼數量、首尾頁/上下頁/省略號智能顯示、多分頁共存區分等企業級特性。
-p(\d+) 模式)$link='' 時輸出 pid/optype 屬性,前端 JS 直接綁定事件$type 參數區分同頁面不同分區.g_serpage、.g_serpagcur、.gpage_nobor 便於樣式定制<?php
/**
* 通用分頁生成類
* 支援靜態化 URL、AJAX、多種顯示模式
*/
class Paginator {
/**
* 生成分頁 HTML
* @param string $link 基礎鏈接(靜態化 URL),空字串表示 AJAX 模式
* @param int $currentPage 當前頁碼(從 1 開始)
* @param int $totalPages 總頁數
* @param int $windowSize 顯示頁碼數量(預設 3,即當前頁前後各 1 頁)
* @param string $type 分頁區域標識(同頁多個分頁時區分)
* @return string 分頁 HTML 代碼
*/
public static function render(
string $link = '',
int $currentPage,
int $totalPages,
int $windowSize = 3,
string $type = ''
): string {
// 參數校驗
$currentPage = max(1, min($currentPage, max(1, $totalPages)));
$windowSize = max(1, $windowSize);
$html = '<div class="g_serpage clearfix">';
if ($totalPages > 0) {
// 上一頁
$html .= self::buildPageLink($link, $type, $currentPage - 1, $currentPage > 1, '<<上一頁');
// 頁碼窗口邏輯
$halfWindow = floor($windowSize / 2);
$startPage = max(1, $currentPage - $halfWindow);
$endPage = min($totalPages, $startPage + $windowSize - 1);
// 調整起始頁確保窗口大小固定(除非總頁數不足)
if ($endPage - $startPage + 1 < $windowSize) {
$startPage = max(1, $endPage - $windowSize + 1);
}
// 首頁與前置省略號
if ($startPage > 1) {
$html .= self::buildPageLink($link, $type, 1, true, '1');
if ($startPage > 2) {
$html .= '<a class="gpage_nobor" href="javascript:void(0)">…</a>';
}
}
// 頁碼列表
for ($i = $startPage; $i <= $endPage; $i++) {
$isCurrent = ($i === $currentPage);
$html .= self::buildPageLink($link, $type, $i, true, (string)$i, $isCurrent);
}
// 尾頁與後置省略號
if ($endPage < $totalPages) {
if ($endPage < $totalPages - 1) {
$html .= '<a class="gpage_nobor" href="javascript:void(0)">…</a>';
}
$html .= self::buildPageLink($link, $type, $totalPages, true, (string)$totalPages);
}
// 下一頁
$html .= self::buildPageLink($link, $type, $currentPage + 1, $currentPage < $totalPages, '下一頁>>');
}
$html .= '</div>';
return $html;
}
/**
* 構建單個頁碼鏈接
*/
private static function buildPageLink(
string $link,
string $type,
int $page,
bool $enabled,
string $text,
bool $isCurrent = false
): string {
$class = $isCurrent ? ' class="g_serpagcur"' : '';
$disabled = !$enabled ? ' class="gpage_nobor"' : '';
$attr = $disabled ?: $class;
if ($link !== '') {
// 靜態化 URL 模式:替換 -p{數字} 為 -p{目標頁}
$url = preg_replace('/-p\d+/', "-p{$page}", $link);
// 若原鏈接無 -p 參數,補充
if (strpos($link, '-p') === false) {
$url = rtrim($link, '/') . "-p{$page}" . (pathinfo($link, PATHINFO_EXTENSION) ? '' : '.html');
}
return "<a href=\"{$url}\"{$attr}>{$text}</a>";
} else {
// AJAX 模式:輸出 data-* 屬性供 JS 綁定
$optype = $type ? " optype=\"{$type}\"" : '';
return "<a{$attr} pid=\"showpage_{$page}\"{$optype} href=\"javascript:void(0)\">{$text}</a>";
}
}
}
<?php
// 假設當前 URL: /article/list-p3.html
$currentPage = isset($_GET['p']) ? (int)$_GET['p'] : 1;
$totalPages = 25;
// 直接輸出
echo Paginator::render('/article/list-p1.html', $currentPage, $totalPages, 5);
// 輸出包含正確 href 的分頁鏈接
<?php
// 後端僅輸出分頁結構,不包含 href
echo Paginator::render('', $currentPage, $totalPages, 5, 'article_list');
<script>
$(document).on('click', '.g_serpage a[pid]', function(e) {
e.preventDefault();
var $this = $(this);
var page = $this.attr('pid').replace('showpage_', '');
var type = $this.attr('optype') || 'default';
// 發送 AJAX 請求
$.get('/api/article/list', { p: page, type: type }, function(html) {
$('#article-container').html(html);
// 更新 URL 不刷新(可選)
history.pushState(null, '', '/article/list-p' + page + '.html');
});
});
</script>
<?php
// 文章列表分頁
echo Paginator::render('/article/list-p1.html', $page1, $total1, 3, 'articles');
// 評論列表分頁(同頁面)
echo Paginator::render('/comment/list-p1.html', $page2, $total2, 3, 'comments');
optype 參數將分別輸出 optype="articles" 和 optype="comments",前端 JS 可據此區分請求目標。
<style>
.g_serpage { text-align: center; margin: 20px 0; font-size: 14px; }
.g_serpage a {
display: inline-block;
padding: 6px 12px;
margin: 0 2px;
border: 1px solid #ddd;
color: #333;
text-decoration: none;
border-radius: 3px;
background: #fff;
}
.g_serpage a:hover { border-color: #007bff; color: #007bff; }
.g_serpage .g_serpagcur {
background: #007bff;
border-color: #007bff;
color: #fff;
cursor: default;
}
.g_serpage .gpage_nobor {
border: none;
background: none;
cursor: default;
color: #999;
}
.g_serpage .gpage_nobor:hover { color: #999; }
</style>
LengthAwarePaginator、ThinkPHP paginate() 可直接輸出兼容結構{$paginator|render}rel="prev/next"、Canonical 標籤這個分頁類經過多個項目實戰驗證,靈活支援傳統偽靜態與現代 SPA 混合架構。核心設計原則:邏輯與輸出分離、URL 模式自適應、前後端契約標準化。可直接複製到項目中使用,或根據業務擴展為更豐富的分頁組件庫。