首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

PHP MySQL 分頁類實戰(基於 mysql_* 扩展):原理、代碼與避坑指南

     編輯:MySQL 資料庫相關知識

背景與定位

本類針對**舊項目維護**、**教學演示**、**快速原型**場景,封裝 mysql_* 系列函數實現分頁。**生產新項目嚴禁使用**,應改用 PDO / MySQLi / MySQLnd,具備預處理、事務、異步、更好性能與安全性。

核心類代碼

<?php
class PageUtil4MySQL {
    private $pageSize = 10;
    public $currPageNum = 1;
    public $countOfCurrPage = 0;
    private $resultList = [];
    public $totalPageNum = 0;
    public $totalRecordNum = 0;
    public $startRowNum = 0;
    public $errorMsg = '';

    public function __construct($pageSize = 10) {
        $this->pageSize = max(1, (int)$pageSize);
    }

    public function setPageSize($pageSize) {
        $this->pageSize = max(1, (int)$pageSize);
        return $this;
    }

    /**
     * 獲取指定頁數據
     * @param int $pageNum 頁碼(從 1 開始)
     * @param string $selectStr 完整 SELECT 語句(不含 LIMIT)
     * @param int $fetchStyle MYSQL_ASSOC | MYSQL_NUM | MYSQL_BOTH
     * @return array 指定頁的記錄數組
     */
    public function resultListOfPage($pageNum, $selectStr, $fetchStyle = MYSQL_ASSOC) {
        $this->resultList = [];
        $this->currPageNum = (int)$pageNum;

        // 1. 計算總行數(子查詢替換 SELECT 列表為 COUNT(*))
        $totalRowNum = $this->getRowCount($selectStr);
        $this->totalRecordNum = $totalRowNum;

        if ($totalRowNum > 0) {
            $this->totalPageNum = (int)ceil($totalRowNum / $this->pageSize);

            // 2. 頁碼邊界修正
            if ($this->currPageNum < 1 || $this->currPageNum > $this->totalPageNum) {
                $this->currPageNum = 1;
            }

            // 3. 拼接 LIMIT 子句
            $this->startRowNum = ($this->currPageNum - 1) * $this->pageSize;
            $selectStr .= " LIMIT {$this->startRowNum}, {$this->pageSize}";

            // 4. 執行查詢
            $rs = @mysql_query($selectStr);
            if (!$rs) {
                $this->errorMsg = mysql_error();
                return [];
            }

            while ($row = mysql_fetch_array($rs, $fetchStyle)) {
                $this->resultList[] = $row;
            }
            mysql_free_result($rs);
            $this->countOfCurrPage = count($this->resultList);
        }
        return $this->resultList;
    }

    /**
     * 通過正則將 SELECT ... FROM 轉為 SELECT COUNT(*) FROM,並移除 ORDER BY
     */
    private function getRowCount($selectStr) {
        $selectStr = trim($selectStr);
        // 替換 SELECT 列表
        $selectStr = preg_replace('/^SELECT\s+.*?\s+FROM/is', 'SELECT COUNT(*) FROM', $selectStr);
        // 移除 ORDER BY
        $selectStr = preg_replace('/\s+ORDER\s+BY\s+.*$/is', '', $selectStr);
        $rs = @mysql_query($selectStr);
        if (!$rs) return 0;
        $row = mysql_fetch_array($rs, MYSQL_NUM);
        mysql_free_result($rs);
        return (int)$row[0];
    }
}
?>

使用範例(配合 Smarty 模板)

<?php
require_once 'PageUtil4MySQL.php';

$pageSize = isset($_GET['pageSize']) ? (int)$_GET['pageSize'] : 10;
$page     = isset($_GET['page'])     ? (int)$_GET['page']     : 1;

$selectStr = "SELECT * FROM users ORDER BY id DESC";

$pageUtil = new PageUtil4MySQL($pageSize);
$rsltList = $pageUtil->resultListOfPage($page, $selectStr);

$totalPage      = $pageUtil->totalPageNum;
$currPageNum    = $pageUtil->currPageNum;
$totalRecordNum = $pageUtil->totalRecordNum;
$countOfCurrPage= $pageUtil->countOfCurrPage;
?>

<!-- 分頁導航 -->
<?php if ($totalPage > 1): ?>
  <div style="text-align:center;margin:10px;">
    共  頁,當前第  頁,共  條
    <a href="?page=1&pageSize=">首頁
    <a href="?page=&pageSize=">上一頁
    <a href="?page=&pageSize=">下一頁
    <a href="?page=&pageSize=">尾頁
  </div>
<?php endif; ?>

<!-- 數據列表 -->
<table>
  <?php foreach ($rsltList as $user): ?>
    <tr>
      <td></td>
      <td></td>
      <td></td>
    </tr>
  <?php endforeach; ?>
</table>

關鍵避坑指南

  1. SQL 注入風險:$selectStr 直接拼接,調用方必須確保 WHERE 條件已參數化或經過嚴格過濾。建議類內部改用 mysql_real_escape_string 或強制要求預處理(需升級 MySQLi/PDO)。
  2. getRowCount() 正則局限:不支援巢狀子查詢、UNION、CTE、帶別名的複雜 SELECT。複雜查詢建議手動傳入 COUNT(*) SQL 或單獨查總數。
  3. 大偏移量性能:LIMIT 100000, 10 仍會掃描前 10 萬行。大數據量必須改用游標分頁(WHERE id > last_id LIMIT 10)或覆蓋索引優化。
  4. 持久連接優化:若必須用 mysql_*,請用 mysql_pconnect() 建立連接池效果,減少握手開銷。
  5. 字符集:連接後執行 mysql_query("SET NAMES utf8mb4"),防止中文亂碼。

升級路徑建議

當前目標關鍵改動
mysql_*MySQLi (面向過程)函數名加 i、連接對象前置、預處理 prepare/bind_param
mysql_*PDO統一介面、支援多庫、異常處理、命名佔位符
mysql_*MySQLnd (原生驅動)PHP 5.3+ 內建,替換 libmysqlclient,內存更少、功能更全

總結

本分頁類作為**過渡期維護工具**或**教學樣本**有其價值,展示了分頁核心邏輯:總數統計 → 邊界修正 → LIMIT 拼接 → 結果集封裝。但請牢記:新項目零容忍 mysql_*,技術債務最終都要還。

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