PHP for SQL Server 的Microsoft驅動程式包含兩個 PHP 擴充套件,分別為 SQLSRV 與 PDO_SQLSRV。 PDO_SQLSRV實作了 PHP 資料物件(PDO)介面。 這兩個擴充套件都使用 Microsoft ODBC 驅動程式 for SQL Server 來連接 Microsoft SQL。 他們的 PHP API、驅動程式專屬選項及預設 PHP 類型有所不同。 例如,SQLSRV 預設會以物件形式回傳日期和時間值 DateTime ,而 PDO_SQLSRV 預設會回傳字串。 關於系統需求,請參見 Microsoft PHP for SQL Server 驅動程式的系統需求。
選擇 SQLSRV 或 PDO_SQLSRV
| 如果你... | 使用 |
|---|---|
| 開始一個其他地方沒有 PDO 程式碼的新專案 | 選擇你的應用程式會一致使用的介面。 SQLSRV 會提供程序式函式和明確的參數描述符。 PDO_SQLSRV 適合用於以 PDOStatement 和 PDO 物件為中心組織的程式碼。 |
| 已在另一個資料庫(MySQL、PostgreSQL、SQLite)中使用 PDO | PDO_SQLSRV,這樣你的資料存取層才能在不同資料庫間保持一致。 |
| 使用需要 PDO 連線物件的框架或函式庫 | PDO_SQLSRV. |
| 需要手動、逐區塊控制串流資料何時傳送 | SQLSRV,搭配 sqlsrv_send_stream_data()。 PDO_SQLSRV 也會串流大型值,方法是將 PHP 串流資源綁定為 PDO::PARAM_LOB,但它會在 execute() 期間自動傳送整個串流,而非由呼叫者控制分塊傳送。 |
維護一個已會呼叫 sqlsrv_* 函式的應用程式 |
SQLSRV,除非你打算重寫它的資料存取程式碼以支援 PDO。 |
安裝其中一個擴充功能或兩個都安裝。 Windows 下載檔案包含獨立的擴充功能二進位檔;在 Linux 和 macOS 上,需分別安裝 sqlsrv 和 pdo_sqlsrv 套件。 你可以在同一個 PHP 程序中載入兩者。 SQLSRV 回傳連線與語句資源;PDO_SQLSRV回傳 PDO 與 PDOStatement 物件。 不要把資源從一個 API 傳到另一個 API。
使用 PDO 並不代表 Transact-SQL(T-SQL)語法可以移植到其他資料庫。 PDO 提供通用的 PHP 介面;你的查詢、結構、認證和驅動程式專屬屬性仍可依賴 Microsoft SQL。 同樣地,使用 procedural 擴充功能也不需要整個應用程式都採用程序式寫法:你可以從自己的類別呼叫 SQLSRV。
在兩個 API 中比較相同的結果
在選擇應用程式預期的 PHP 表示方式之前,先執行這些程式。 會選取相同的整數、日期和 SQL NULL,但預設值並不相同。 不需要資料表或範例資料庫。
安裝並載入所選擴充功能。 在程序環境中為現有的 SQL Server 測試資料庫設置 SQL_SERVER、SQL_DATABASE、SQL_USER 和 SQL_PASSWORD。 伺服器憑證必須被信任且與已設定的端點相符。 關於設定與 Azure 的無密碼替代方案,請參見連接伺服器。
SQLSRV
將此完整程式儲存為 compare-sqlsrv.php,並執行 php compare-sqlsrv.php。
<?php
$config = [];
foreach (['SQL_SERVER', 'SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Set $name before running this example.");
}
$config[$name] = $value;
}
foreach (['SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$config[$name] = '{' . str_replace('}', '}}', $config[$name]) . '}';
}
$conn = sqlsrv_connect($config['SQL_SERVER'], [
'Database' => $config['SQL_DATABASE'],
'UID' => $config['SQL_USER'],
'PWD' => $config['SQL_PASSWORD'],
'Encrypt' => true,
'TrustServerCertificate' => false,
'LoginTimeout' => 30,
'CharacterSet' => 'UTF-8',
]);
if ($conn === false) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
$stmt = null;
try {
$stmt = sqlsrv_query($conn, "SELECT CAST(42 AS int) AS CountValue, "
. "CAST('2026-10-19' AS date) AS DayValue, CAST(NULL AS int) AS Missing");
if ($stmt === false) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if ($row === false) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
if (!is_array($row) || $row['CountValue'] !== 42
|| !($row['DayValue'] instanceof DateTime)
|| $row['DayValue']->format('Y-m-d') !== '2026-10-19'
|| $row['Missing'] !== null) {
throw new RuntimeException('Unexpected SQLSRV values or default types.');
}
echo get_debug_type($row['CountValue']), ', ',
get_debug_type($row['DayValue']), ', ', get_debug_type($row['Missing']), PHP_EOL;
} finally {
if (is_resource($stmt)) {
sqlsrv_free_stmt($stmt);
}
sqlsrv_close($conn);
}
預期產出:
int, DateTime, null
PDO_SQLSRV
將此獨立程式儲存為 compare-pdo.php,並執行 php compare-pdo.php。 它刻意將數值和日期的擷取保留為 PDO_SQLSRV 的預設值。
<?php
$config = [];
foreach (['SQL_SERVER', 'SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Set $name before running this example.");
}
$config[$name] = $value;
}
foreach (['SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$config[$name] = '{' . str_replace('}', '}}', $config[$name]) . '}';
}
$server = $config['SQL_SERVER'];
$database = $config['SQL_DATABASE'];
$pdo = new PDO(
"sqlsrv:Server=$server;Database=$database;Encrypt=true;"
. "TrustServerCertificate=false;LoginTimeout=30",
$config['SQL_USER'],
$config['SQL_PASSWORD'],
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_UTF8,
]
);
$stmt = null;
try {
$stmt = $pdo->query("SELECT CAST(42 AS int) AS CountValue, "
. "CAST('2026-10-19' AS date) AS DayValue, CAST(NULL AS int) AS Missing");
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row !== ['CountValue' => '42', 'DayValue' => '2026-10-19', 'Missing' => null]) {
throw new RuntimeException('Unexpected PDO_SQLSRV values or default types.');
}
echo get_debug_type($row['CountValue']), ', ',
get_debug_type($row['DayValue']), ', ', get_debug_type($row['Missing']), PHP_EOL;
} finally {
$stmt = null;
$pdo = null;
}
預期產出:
string, string, null
兩個程式都明確請求命名的結果鍵。 這些 PHP 陣列使用欄位名稱,如 CountValue,而非以零為基礎的數字索引。 SQL NULL 在任一 API 中都會變成 PHP null。 它不是空字串或缺少的結果列。
兩個程式都會從環境中讀取資料庫名稱、使用者名稱和密碼。 它們會將這些值跳脫一次,供 PHP 驅動程式的 ODBC 式連線處理使用。 雙重閉合大括號並將值包在大括號中,可以保留特殊字元;它不會重新命名資料庫或改變儲存的密碼。 更多資訊請參見 「連接伺服器」。
範例在發生錯誤時停止,並在 finally 中釋放資源。 SQLSRV 透過 false 和 sqlsrv_errors() 回報資料庫失敗;PDO_SQLSRV 會在連線失敗時擲回錯誤,並在已設定的錯誤模式下,針對查詢失敗使用例外狀況。 在網頁應用程式中,請在處理請求的程式碼中捕捉這些例外。 將診斷細節記錄在受保護的日誌中,而非回傳到瀏覽器。
練習結束後,如果你不再需要它們,請刪除 compare-sqlsrv.php 和 compare-pdo.php。 完成後,從測試程序中清除憑證環境變數。
選擇類型、編碼與錯誤處理
決定你的應用程式預期使用哪些 PHP 類型、如何處理文字和錯誤,以及何時釋出資料庫資源。
| 決策 | SQLSRV | PDO_SQLSRV | 對應用程式碼的影響 |
|---|---|---|---|
| 整數結果類型 | SQL int 欄位的原生 PHP 整數。 | 預設為字串;PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE 要求支援的數值 PHP 類型。 |
嚴格比較與序列化輸出可能有所不同。 不要假設每個 SQL 的數值類型都可用 PHP 整數或浮點數表示。 |
| 日期與時間結果 |
DateTime 預設為物件;ReturnDatesAsStrings 選擇字串。 |
預設為字串;PDO::SQLSRV_ATTR_FETCHES_DATETIME_TYPE 選擇物件。 |
在序列化或傳給應用程式程式碼前,先將表示法正規化。 |
| 字元編碼 | 明確設定 CharacterSet => 'UTF-8' 。 |
預設為 UTF-8;設定為 PDO::SQLSRV_ATTR_ENCODING。 |
不要把 SQLSRV 選項傳遞到 PDO DSN。 |
| 錯誤處理 | 立即檢查 false 並擷取完整的 SQLSRV 錯誤集合。 |
配置 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION。 |
缺失的資料列和失敗的查詢需要不同的處理方式。 |
| 參數重用 | 在描述符中以參照方式綁定變數。 | 使用 bindParam() 表示變數,或使用 bindValue() 表示其目前值。 |
重新指派變數影響的是參考綁定,而非先前的值綁定。 |
| Cleanup | 釋放陳述式,然後關閉連線資源。 | 先參照 release statement,然後是 PDO 物件。 | 一個語句可以在另一個參考被設定為 null 後,仍能維持 PDO 連線的存續。 |
如果你遷移現有應用程式,請先用代表性的數值測試這些行為,再修改其資料庫程式碼。 包含非 ASCII 文字、SQL NULL、日期、精確十進位值及儲存程序輸出參數。 也要比較你的框架如何配置 PDO 屬性。 僅僅替換函式名稱並不能遷移錯誤處理、參考綁定或回傳的 PHP 型別。
若想使用包含兩種 API 的寫入與讀取範例工作流程,請參閱 程式設計指南。 對於重複執行和鍵入的輸入,請使用 Compare query execution functions。
了解每個元件的功能
你的擴充功能選擇會改變 PHP API 及其型別轉換。 這兩個擴充功能都使用 ODBC 與伺服器通訊、加密連線及進行驗證。 作業系統提供受信任的憑證與服務,如 Windows 驗證 或 Kerberos。 SQL 目標控制權限、結構以及支援的 T-SQL 語句。
比較程式使用 SQL 認證來連接 SQL Server。 對於 Azure SQL Database、Azure SQL 受控執行個體、Azure Synapse Analytics、Fabric 中的 SQL 資料庫,或 Fabric 中的 Warehouse,請先設定服務端點並啟用支援的認證,再重新使用擷取模式。 這兩個 Fabric 產品都需要 Microsoft Entra ID 認證;Warehouse 也需要停用多個主動結果集(MARS)。 在 SQL Server 上成功進行型別比較,並不代表不同目標支援本概述中列出的所有功能。
兩個擴充包都能提供什麼
- 整合認證、SQL 認證,以及根據目標支援的 Microsoft Entra ID 認證。 管理身份不使用應用程式密碼;
ActiveDirectoryServicePrincipal使用用戶端 ID 和用戶端秘密。 更多資訊請參見 「連接伺服器」。 - 參數化查詢、預先準備的語句,以及方向性(輸入/輸出)參數。 更多資訊請參閱「 執行函數比較 」及「 使用方向參數」。
- 交易、用於大型字元與二進位值的串流輸入參數,以及表格值參數。
- 始終加密,包括 ODBC 驅動程式與 SQL Server 目標支援的安全隔區。 欲了解更多資訊,請參閱《使用 Microsoft Drivers for PHP for SQL Server 的 Always Encrypted》。
- 連接至 SQL Server、Azure SQL Database、Azure SQL 受控執行個體、Azure Synapse Analytics、Microsoft Fabric 中的 SQL 資料庫,以及 Microsoft Fabric 中的 Warehouse。 認證與 SQL 功能依目標而異;詳見 Azure 與 Fabric 連線。