PHP 連接 Microsoft SQL Server (MSSQL) 讀寫中文出現亂碼(問號、方塊、亂碼字符),核心原因是**編碼鏈路不一致**。本文提供「數據庫/表/列、連接層、PHP 文件、瀏覽器輸出」四層編碼統一檢查表,並給出 sqlsrv/pdo_sqlsrv、FreeTDS/ODBC 兩大驅動的具體配置代碼。
編碼統一四層檢查表
| 層級 | 檢查項 | 標準值 | 驗證方法 |
| 1. 數據庫對象 | 數據庫、表、列 校對規則 | Chinese_PRC_CI_AS / Latin1_General_CI_AS(支援 Unicode 的列用 NVARCHAR) | SELECT name, collation_name FROM sys.columns WHERE object_id = OBJECT_ID('dbo.YourTable') |
| 2. 連接層 | 連接字符串 / DSN 指定字符集 | CharacterSet = UTF-8 / ClientCharset = UTF-8 | 代碼中顯式設置 |
| 3. PHP 文件 | 源文件編碼 | UTF-8 無 BOM | 編輯器右下角 / file 命令 |
| 4. 瀏覽器輸出 | HTTP Header / Meta 標籤 | Content-Type: text/html; charset=utf-8 | Network 面板檢查 Response Headers |
方案一:Microsoft 官方驅動 sqlsrv / pdo_sqlsrv (Windows / Linux 均支援)
安裝
# Windows: 下載 DLL 放入 ext/, php.ini 啟用 extension=php_sqlsrv_81_ts.dll
# Linux (Ubuntu):
curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
apt update && ACCEPT_EULA=Y apt install msodbcsql18 mssql-tools18
pecl install sqlsrv pdo_sqlsrv
echo "extension=sqlsrv.so\nextension=pdo_sqlsrv.so" > /etc/php/8.1/mods-available/sqlsrv.ini
phpenmod sqlsrv
連接代碼(關鍵:CharacterSet = UTF-8)
<?php
// sqlsrv 過程式風格
$serverName = "127.0.0.1,1433";
$connectionInfo = [
"UID" => "sa",
"PWD" => "YourStrongPass123",
"Database" => "MyDB",
"CharacterSet" => "UTF-8", // ★ 關鍵:告訴驅動以 UTF-8 傳輸
"ReturnDatesAsStrings" => true,
];
$conn = sqlsrv_connect($serverName, $connectionInfo);
if (!$conn) die(print_r(sqlsrv_errors(), true));
// 查詢
$sql = "SELECT N'中文測試' AS col1, col2 FROM dbo.TestTable";
$stmt = sqlsrv_query($conn, $sql);
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
echo $row['col1'] . "\n"; // 正確輸出中文
}
sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
?>
PDO 風格(推薦)
<?php
$dsn = "sqlsrv:Server=127.0.0.1,1433;Database=MyDB;CharacterSet=UTF-8";
$pdo = new PDO($dsn, "sa", "YourStrongPass123", [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // 使用真實預處理
]);
$stmt = $pdo->query("SELECT N'中文' AS txt");
echo $stmt->fetchColumn(); // 正確輸出
?>
方案二:FreeTDS + ODBC (傳統 Linux 方案,漸被官方驅動取代)
freetds.conf 關鍵配置
# /etc/freetds/freetds.conf
[global]
tds version = 8.0 # TDS 8.0 支援 Unicode
client charset = UTF-8 # ★ 關鍵:客戶端字符集
text size = 64512
[mssql_server]
host = 192.168.1.100
port = 1433
tds version = 8.0
client charset = UTF-8
ODBC DSN 配置 (/etc/odbc.ini)
[MSSQL]
Driver = FreeTDS
Description = MSSQL via FreeTDS
Server = mssql_server
Database = MyDB
TDS_Version = 8.0
ClientCharset = UTF-8
PHP 連接
<?php
$conn = odbc_connect("MSSQL", "sa", "password");
if (!$conn) die(odbc_errormsg());
// 查詢前設置編碼(某些版本需要)
odbc_exec($conn, "SET NAMES 'utf8'"); // 不一定生效,視驅動而定
$result = odbc_exec($conn, "SELECT col FROM dbo.Table");
while (odbc_fetch_row($result)) {
echo odbc_result($result, "col") . "\n";
}
odbc_close($conn);
?>
常見坑位與解決
| 現象 | 原因 | 解決 |
| 讀出中文是 ??? 或 | 連接層未指定 UTF-8,驅動按默認 Latin1 傳輸 | 連接信息加 CharacterSet=UTF-8 / ClientCharset=UTF-8 |
| 寫入中文變亂碼 | 列類型為 VARCHAR 非 NVARCHAR,無法存儲 Unicode | 改用 NVARCHAR(n),或確保數據庫校對規則支援該字符集 |
| PHP 文件編碼非 UTF-8 | 編輯器保存為 GBK/ANSI | 另存為 UTF-8 無 BOM |
| 瀏覽器顯示亂碼 | HTTP Header 缺少 charset | header('Content-Type: text/html; charset=utf-8') |
| sqlsrv 驅動加載失敗 | DLL 版本不匹配(TS/NTS、x86/x64、PHP 版本) | 下載對應版本:php_sqlsrv_81_ts_x64.dll 等 |
最佳實踐總結
- 數據庫列**統一使用 NVARCHAR** 存儲多語言文本。
- 連接字符串**顯式指定 CharacterSet=UTF-8 / ClientCharset=UTF-8**。
- PHP 源文件**UTF-8 無 BOM**,頁面輸出 **header charset=utf-8**。
- 優先使用 **Microsoft 官方 sqlsrv/pdo_sqlsrv 驅動**,免去 FreeTDS/ODBC 多層轉碼麻煩。
- 使用 **PDO + 預處理語句**,統一錯誤處理、參數綁定、跨驅動可移植。