首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

Discuz! 分頁函數 multi() 深度解析與現代化重構建議

     編輯:PHP 綜合知識

原始碼回顧

Discuz! 經典的 multi() 函數(位於 global.func.php)用於生成論壇列表、帖子列表等頁面的分頁導航 HTML。以下為核心邏輯重點:

function multi($num, $perpage, $curpage, $mpurl, $maxpages = 0) {
    $multipage = '';
    $mpurl .= strpos($mpurl, '?') ? '&' : '?';

    if ($num > $perpage) {
        $page = 10;      // 顯示頁碼數量
        $offset = 2;     // 當前頁左右偏移

        $realpages = ceil($num / $perpage);
        $pages = ($maxpages && $maxpages < $realpages) ? $maxpages : $realpages;

        // 計算顯示範圍 $from ~ $to
        if ($page > $pages) { $from = 1; $to = $pages; }
        else {
            $from = $curpage - $offset;
            $to = $from + $page - 1;
            if ($from < 1) { $to = $curpage + 1 - $from; $from = 1; if ($to - $from < $page) $to = $page; }
            elseif ($to > $pages) { $from = $pages - $page + 1; $to = $pages; }
        }

        // 組裝 HTML:首頁、上一頁、頁碼、下一頁、尾頁、跳轉輸入框
        $multipage = ($curpage - $offset > 1 && $pages > $page)
            ? '<a class="p_redirect" href="'.$mpurl.'page=1">|<</a>' : '';
        $multipage .= ($curpage > 1)
            ? '<a class="p_redirect" href="'.$mpurl.'page='.($curpage-1).'">&laquo;</a>' : '';

        for ($i = $from; $i <= $to; $i++) {
            $multipage .= ($i == $curpage)
                ? '<a class="p_curpage">'.$i.'</a>'
                : '<a href="'.$mpurl.'page='.$i.'" class="p_num">'.$i.'</a>';
        }

        $multipage .= ($curpage < $pages)
            ? '<a class="p_redirect" href="'.$mpurl.'page='.($curpage+1).'">&raquo;</a>' : '';
        $multipage .= ($to < $pages)
            ? '<a class="p_redirect" href="'.$mpurl.'page='.$pages.'">>|</a>' : '';

        // 最大頁限制提示與跳轉輸入框
        $multipage .= ($curpage == $maxpages)
            ? '<a class="p_redirect" href="misc.php?action=maxpages&pages='.$maxpages.'">&raquo;</a>' : '';
        $multipage .= ($pages > $page)
            ? '<a class="p_pages" style="padding:0"><input class="p_input" type="text" name="custompage" onKeyDown="if(event.keyCode==13){window.location=\''.$mpurl.'page=\'+this.value;return false;}"></a>' : '';

        $multipage = $multipage
            ? '<div class="p_bar"><a class="p_total"> '.$num.' </a><a class="p_pages"> '.$curpage.'/'.$realpages.' </a>'.$multipage.'</div>'
            : '';
    }
    return $multipage;
}

關鍵設計特點

  1. 智慧型頁碼窗口:固定顯示 10 個頁碼($page=10),當前頁置中($offset=2),首尾邊界自動調整。
  2. 語義化 CSS 類別:p_redirect(首/尾/上/下)、p_curpage(當前)、p_num(普通頁碼)、p_bar(容器),便於樣式客製。
  3. 無障礙跳轉輸入框:Enter 鍵直接跳轉,提升大量頁面體驗。
  4. URL 參數自動補全:自動判斷 $mpurl 是否含 ?,決定用 ? 或 & 串接 page=。

潛在問題與現代化重構建議

問題建議改進
直接輸出 HTML,難以複用/測試改為回傳結構化陣列(['pages'=>[...], 'current'=>N, 'total'=>M, 'url_template'=>'...']),由視圖層渲染
內聯 JavaScript (onKeyDown)採用 addEventListener 綁定,支援 CSP
硬編碼樣式類別允許傳入類別名稱前綴或模板回呼
不支援 AJAX 分頁提供 data-page 屬性,前端自行攔截
使用舊式 ereg 等棄用函數(其他處)全面遷移至 preg_* / filter_var

重構範例:結構化輸出版

function pager(array $opts = []) {
    $defaults = [
        'total'      => 0,      // 總筆數
        'per_page'   => 20,     // 每頁筆數
        'current'    => 1,      // 當前頁
        'url_tpl'    => '?page=%d', // URL 模板,%d 會被頁碼替換
        'window'     => 10,     // 顯示頁碼數
        'max_pages'  => 0,      // 最大頁數限制
    ];
    $o = array_merge($defaults, $opts);

    $real_pages = (int)ceil($o['total'] / $o['per_page']);
    $pages = $o['max_pages'] && $o['max_pages'] < $real_pages ? $o['max_pages'] : $real_pages;
    if ($pages <= 1) return ['pages' => [], 'current' => 1, 'total' => $real_pages, 'html' => ''];

    $half = (int)($o['window'] / 2);
    $from = max(1, $o['current'] - $half);
    $to = min($pages, $from + $o['window'] - 1);
    if ($to - $from + 1 < $o['window']) $from = max(1, $to - $o['window'] + 1);

    $items = [];
    if ($o['current'] > 1) $items[] = ['page' => 1, 'text' => '««', 'rel' => 'first'];
    if ($o['current'] > 1) $items[] = ['page' => $o['current']-1, 'text' => '«', 'rel' => 'prev'];

    for ($i = $from; $i <= $to; $i++) {
        $items[] = [
            'page'   => $i,
            'text'   => (string)$i,
            'current'=> ($i == $o['current']),
            'url'    => sprintf($o['url_tpl'], $i),
        ];
    }

    if ($o['current'] < $pages) $items[] = ['page' => $o['current']+1, 'text' => '»', 'rel' => 'next'];
    if ($o['current'] < $pages) $items[] = ['page' => $pages, 'text' => '»»', 'rel' => 'last'];

    return [
        'items'   => $items,
        'current' => $o['current'],
        'total'   => $real_pages,
        'per_page'=> $o['per_page'],
        'count'   => $o['total'],
    ];
}

// 視圖層渲染(Twig / Blade / 原生 PHP)
/*
<nav aria-label="Pagination">
  <ul class="pagination">
    <?php foreach ($pager['items'] as $it): ?>
      <li class="page-item <?= $it['current'] ? 'active' : '' ?>">
        <?php if ($it['current']): ?>
          <span class="page-link"><?= $it['text'] ?></span>
        <?php else: ?>
          <a class="page-link" href="<?= $it['url'] ?>" <?= isset($it['rel']) ? 'rel="'.$it['rel'].'"' : '' ?>><?= $it['text'] ?></a>
        <?php endif; ?>
      </li>
    <?php endforeach; ?>
  </ul>
</nav>
*/

結語

Discuz! 的 multi() 在當年是極佳的實用主義作品,但現代架構強調關注點分離與可測試性。將分頁邏輯拆分為「計算資料結構」與「渲染 HTML」兩階段,並配合語義化 HTML5 與無障礙屬性(aria-label、rel),能更好地適應響應式設計、前端框架整合與自動化測試需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved