用於 SQL Server 之 PHP 的 Microsoft 驅動程式

下載 PHP 驅動程式

Microsoft PHP for SQL Server 驅動程式是 PHP 擴充功能,允許你從 PHP 腳本讀寫 Microsoft SQL 資料庫引擎 中的資料。 該套件附帶兩個驅動程式,這些驅動程式都包裹相同的 Microsoft ODBC 驅動程式 for SQL Server,且共享相同的連線選項,因此你可以選擇適合你程式碼庫的 API:

  • SQLSRV 會暴露一個針對 SQL Server 功能量身打造的程序式 API(sqlsrv_*函式)。
  • PDO_SQLSRV實作了 PHP 資料物件(PDO)介面,因此已使用 PDO 用於其他資料庫的程式碼也能以最小的修改來鎖定SQL Server。

兩個驅動程式都連接 Azure SQL Database、Microsoft Fabric 中的 SQL 資料庫、Azure SQL 受控執行個體,以及所有支援的 SQL Server 版本(包括 Express 版本)。 它們使用 PHP 串流來移動大型二進位和字元值,且不會完全載入記憶體。

選擇你的起點

Azure SQL 的生產環境基準

使用此片段作為與 PDO_SQLSRV 驅動程式建立生產導向Azure SQL連線的起點。 它會從環境變數(例如 Azure App 服務 應用程式設定)讀取伺服器和資料庫,使用管理身份驗證,啟用傳輸層安全(TLS)並驗證伺服器憑證,設定登入逾時以涵蓋冷啟動故障轉移,並設定 ConnectRetryCountConnectRetryInterval SQL Server 閒置連線韌性。 應用程式層 connectWithRetry 級與 queryWithRetry 輔助工具會以有界指數退讓包覆初始連線與每個語句,並將暫態連線錯誤(需要重新連線)與暫態查詢錯誤(重複使用同一連線)區分開來。

需要 PHP 8.0 及更新版本、PDO_SQLSRV 擴充功能,以及 SQL Server 17.3.1.1 及更新版本的 Microsoft ODBC 驅動程式。Authentication=ActiveDirectoryMsi 完整支援Authentication的數值清單,請參見使用 Microsoft Entra 認證連接

<?php
declare(strict_types=1);

// Transient errors that require a fresh connection to recover. SQLSTATE values
// starting with '08' cover ODBC connection-established and connection-broken
// states (for example, 08001, 08S01).
const CONNECT_RETRY_SQLSTATE_PREFIX = '08';

// SQL Server error codes that are transient regardless of when they surface:
// 1205 (deadlock victim), 1222 (lock request timeout), and the Azure SQL
// throttling, mid-query failover, and "database not currently available"
// codes that arrive with SQLSTATE HY000.
const TRANSIENT_SERVER_ERROR_CODES = [1205, 1222, 40501, 40613, 40197, 10928, 10929, 49918];

/**
 * Open a connection, retrying transient failures with exponential backoff.
 */
function connectWithRetry(string $dsn, array $options, int $maxAttempts = 3): PDO
{
    for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
        try {
            $pdo = new PDO($dsn, null, null, $options);
            error_log(sprintf('connected on attempt %d/%d', $attempt, $maxAttempts));
            return $pdo;
        } catch (PDOException $e) {
            $sqlstate = (string) $e->getCode();
            $driverCode = isset($e->errorInfo[1]) ? (int) $e->errorInfo[1] : 0;
            $isTransient = str_starts_with($sqlstate, CONNECT_RETRY_SQLSTATE_PREFIX)
                || in_array($driverCode, TRANSIENT_SERVER_ERROR_CODES, true);
            if (!$isTransient || $attempt === $maxAttempts) {
                error_log(sprintf('connect failed on attempt %d/%d: %s', $attempt, $maxAttempts, $e->getMessage()));
                throw $e;
            }
            $delay = 2 ** ($attempt - 1); // 1, 2, 4 seconds
            error_log(sprintf('connect attempt %d hit transient %s/%d; retrying in %d seconds', $attempt, $sqlstate, $driverCode, $delay));
            sleep($delay);
        }
    }
    throw new RuntimeException('connectWithRetry exhausted retries');
}

/**
 * Run a parameterized query, retrying transient statement failures on the same
 * connection. Deadlocks (1205) roll back the transaction before the driver sees
 * the error, so rerunning a single statement is safe. If the statement was part
 * of a multistatement transaction, wrap the whole transaction in your own retry
 * loop so earlier statements replay too.
 */
function queryWithRetry(PDO $pdo, string $sql, array $params = [], int $maxAttempts = 3): PDOStatement
{
    for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
        try {
            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);
            return $stmt;
        } catch (PDOException $e) {
            $driverCode = isset($e->errorInfo[1]) ? (int) $e->errorInfo[1] : 0;
            $isTransient = in_array($driverCode, TRANSIENT_SERVER_ERROR_CODES, true);
            if (!$isTransient || $attempt === $maxAttempts) {
                error_log(sprintf('query failed on attempt %d/%d: %s', $attempt, $maxAttempts, $e->getMessage()));
                throw $e;
            }
            $delay = 2 ** ($attempt - 1);
            error_log(sprintf('query attempt %d hit transient code %d; retrying in %d seconds', $attempt, $driverCode, $delay));
            sleep($delay);
        }
    }
    throw new RuntimeException('queryWithRetry exhausted retries');
}

// Load endpoint details from application configuration. In Azure App Service,
// these can come from app settings or Key Vault-backed settings.
$server = getenv('SQL_SERVER') ?: null;
$database = getenv('SQL_DATABASE') ?: null;

if ($server === null || $database === null) {
    throw new RuntimeException('Set SQL_SERVER and SQL_DATABASE in your application configuration.');
}

$dsn = sprintf(
    'sqlsrv:Driver={ODBC Driver 18 for SQL Server};Server=%s;Database=%s;'
    . 'Encrypt=true;TrustServerCertificate=false;'
    . 'LoginTimeout=90;Authentication=ActiveDirectoryMsi;'
    . 'ConnectRetryCount=5;ConnectRetryInterval=15;'
    . 'MultiSubnetFailover=true;',
    $server,
    $database
);

$options = [
    PDO::ATTR_ERRMODE               => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE    => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES      => false,
    PDO::SQLSRV_ATTR_QUERY_TIMEOUT  => 30,
];

$pdo = connectWithRetry($dsn, $options);
$stmt = queryWithRetry($pdo, 'SELECT TOP (?) name FROM sys.databases ORDER BY name', [5]);
foreach ($stmt as $row) {
    echo $row['name'], PHP_EOL;
}

此程式碼片段已針對 Azure SQL Database 故障轉移群組和 Azure SQL 受控執行個體 進行最佳化。

  • Driver={ODBC Driver 18 for SQL Server} 會將 ODBC 18 驅動程式版本固定下來。 如果主機同時安裝了 ODBC 17,PDO_SQLSRV 可以綁定到 ODBC 17。 較舊的 17.x 組建版本會拒絕較新的 Authentication 值;例如,Authentication=ActiveDirectoryMsi 需要 ODBC 17.3.1.1 或更新版本。 請參閱 連接字串 屬性「Authentication」所指定的無效值

  • ConnectRetryCountConnectRetryInterval 是 ODBC 連線字串關鍵字,可啟用 SQL Server 的閒置連線恢復能力:驅動程式會以透明方式重新連線中斷的閒置連線。 這與應用程式層 queryWithRetry級不同,後者會重試因僵局或查詢逾時等暫態錯誤而失敗的 語句 。 兩者互補,所以兩者都保留。 請確保 LoginTimeout 至少為 ConnectRetryCount * ConnectRetryInterval,這樣閒置重新連線路徑才能獲得完整的時間預算;範例使用 90 秒,以涵蓋 5 × 15 秒的重試時間,並保留冷容錯移轉時初次登入所需的餘裕。

  • 以驅動程式端診斷來補強應用程式層級的 error_log() 呼叫。 對於 PDO_SQLSRV,設定 pdo_sqlsrv.log_severityphp.ini (僅可於初始化時設定);對於 SQLSRV,則在執行時呼叫 sqlsrv_configure("LogSubsystems", ...) 。 欲了解更多資訊,請參閱 日誌活動

    ; php.ini - enable PDO_SQLSRV driver diagnostics alongside the application-level
    ; error_log() calls in the sample. Use 1 (errors) in production; -1 (all) is
    ; useful during triage but very chatty.
    [pdo_sqlsrv]
    pdo_sqlsrv.log_severity = 1
    
  • 對於 使用者指派 的受控識別,請將該識別的 ID 作為 PDO 的 $username 引數傳遞(new PDO($dsn, $identityId, null, $options))。 在 Azure App 服務Azure Container Instance 上使用該身分的 client ID;否則,使用其 object ID。 PHP 驅動程式會繼承底層 Microsoft ODBC Driver for SQL Server 的此行為;如需詳細資訊,請參閱 使用 Microsoft Entra ID 搭配 ODBC Driver。 PDO_SQLSRV DSN 本身會被 UID 拒絕,所以要用建構槽。 假扮null使用者(如範例所示)選擇系統指派的 Azure 主機管理身份。 對於 SQLSRV(程序式),請在連線選項陣列中傳遞 UID

  • 當您連線到容錯移轉群組接聽程式、可用性群組接聽程式或容錯移轉叢集執行個體端點時,請設定 MultiSubnetFailover=true。 設定此值可提升單子網及多子網可用性群組監聽者的連線效能。 欲了解更多資訊,請參閱 高可用性支援與災難復原

  • 若要讀取擴展或可讀的次級資料,則可加入 ApplicationIntent=ReadOnly 資料來源名稱(DSN)。

  • 對於主權雲端,若憑證主體別名 (SAN) 未包含您要連線到的主機,請將 HostNameInCertificate 加入 DSN(例如,Azure Government 為 *.database.usgovcloudapi.net)。

  • 該驅動程式依賴底層的 Microsoft ODBC 驅動程式 for SQL Server 來取得令牌。 受控識別、服務主體和存取權杖流程皆透過 ODBC 進行。 如需詳細資訊,請參閱搭配使用 Microsoft Entra ID 與 ODBC 驅動程式

  • 為了提升安全性與跨環境的可攜性,請將連線資訊排除在程式碼之外。 將連線資訊儲存在應用程式的設定系統中,並使用 Azure Key Vault 來管理敏感值及集中管理的連線設定。

  • 等效的 SQLSRV 連線會使用 sqlsrv_connect($server, ['Database' => $database, 'Encrypt' => true, 'Authentication' => 'ActiveDirectoryMsi', /* ... */]) 並回傳一個資源。 重試模式相同:捕捉來自 sqlsrv_connectfalse 傳回值,檢查 sqlsrv_errors() 中的 SQLSTATE,並在重試前先延後一段時間。 如需逐步範例,請參閱 步驟 4:使用 PHP 以具復原能力的方式連線至 SQL

  • 重試輔助程式會讀取由 isset() 保護的 $e->errorInfo[1]PDOException::$errorInfo 宣告為 ?array,且預設為 null,因此防禦性檢查會回退使用 0 作為驅動程式代碼,並由 SQLSTATE 08 前綴決定是否重試。

關於此配置各部分的更多資訊,請參見:

如需 Azure SQL 暫態錯誤清單,請參閱 「Troubleshoot transient connection errors」

重點功能

  • 兩個 API 與一個驅動程式套件:程序式 SQLSRV 用於SQL Server優先程式碼,或 PDO_SQLSRV 用於可攜式 PDO 程式碼。
  • 廣泛支援平台:可在 Windows、Linux 及 macOS 上運行,並支援 PHP 版本。
  • 加密連線:透過 Encrypt=true的 TLS 加密連線,伺服器憑證驗證由 TrustServerCertificate控制。
  • Microsoft Entra ID 認證:無密碼連線,包含受管理身份、服務主體及存取權杖,皆透過底層的 Microsoft ODBC 驅動程式 for SQL Server 進行。
  • 始終加密:用戶端加密用於敏感欄位,並可選地設置安全隔區用於原地操作。
  • 連線韌性:內建使用 ConnectRetryCountConnectRetryInterval 的閒置連線重試機制。
  • PHP 串流:將大型二進位與字元值讀寫為串流,而非載入記憶體。
  • 豐富的 SQL Server 資料型別支援datetimeoffset、table-valued parameters、nvarchar 以及帶有 PDO::SQLSRV_ENCODING_UTF8的 Unicode。

開始

文章 Description
系統需求 支援 PHP、作業系統及 SQL Server 版本。
支援矩陣 PHP 驅動程式版本的詳細相容性矩陣。
下載 Microsoft PHP for SQL Server 驅動程式 下載連結並發布相關資料。
Linux 與 macOS 安裝教學 在 Linux 和 macOS 上安裝驅動程式及其 ODBC 前置條件。
載入驅動程式 php.ini 中啟用擴充功能。
開始使用 PHP SQL 驅動程式 端對端逐步解說,將四個入門步驟串接起來。
PHP SQL 驅動程式概述 套件裡有什麼?什麼時候該選擇 SQLSRV 或 PDO_SQLSRV。

設定和連線

文章 Description
連接伺服器 從 PHP 開啟一個連接到 SQL Server 實例的連線。
連線選項 完整參考資料,包含連線關鍵字、預設值及其設定方法。
連線到 Microsoft Azure SQL Database 將 PHP 應用程式連接到 Azure SQL Database。
連接指定的埠口 鎖定非預設的 TCP 埠。
連接共用 在 PHP 請求間重複使用 ODBC 連線。
停用多個主動結果集(MARS) 關閉 MARS 以達成相容性。
LocalDB 的支援 連接到 SQL Server 的 LocalDB 實例。
高可用性支援與災難復原 可用性群組監聽器與多子網故障轉移。
閒置連線韌性 空閒連線斷裂時自動重新連接。

Authenticate

文章 Description
使用 Microsoft Entra 驗證進行連線 管理身份、服務主體、存取權杖和密碼流程。
使用 SQL Server 認證連接 使用包含使用者名稱和密碼的 SQL 登入。
使用 Windows 驗證連線 在網域加入的主機上使用 Windows 整合認證。

Secure

文章 Description
安全性考量 PHP 應用的威脅模型與防禦深度指引。
一直用 PHP 驅動程式加密 為敏感欄位設定客戶端加密。
Always Encrypted 具有安全記憶體保護區 啟用加密欄位的豐富操作,並具備安全區塊。

資料擷取與更新

文章 Description
程式指南 兩種驅動程式的端對端程式設計指南。
執行函數比較 選擇適合你工作負載的執行函式。
直接執行與預備語句執行(PDO_SQLSRV) 何時使用直接執行,何時使用預先陳述。
資料擷取 取得列、欄和串流值。
更新資料 插入、更新和刪除資料列。
執行參數化查詢 綁定參數以防範 SQL 注入。
以串流方式傳送資料 將大型二進位和字元值串流到 SQL Server。
執行交易 將語句組成原子事務。
使用表格值參數 TABLE 參數傳遞給預存程序。
指定游標類型並選擇列 選擇僅前進、靜態、動態或鍵集游標。

數據類型

文章 Description
資料型態轉換 驅動程式如何將 PHP 型別映射到 SQL Server 型別。
預設 SQL Server 資料型態 每個 PHP 值的預設 SQL Server 類型。
預設 PHP 資料型態 每個 SQL Server 欄位類型的預設 PHP 類型。
指定 SQL Server 資料型別(SQLSRV) 綁定參數時覆寫 SQL Server 類型。
指定 PHP 資料型別 擷取時要覆蓋 PHP 類型。
傳送與取回 UTF-8 資料 PDO::SQLSRV_ENCODING_UTF8 用於 Unicode 來回轉換。
在 Linux 和 macOS 上傳送與取回 ASCII 資料 在非 Windows 主機上處理 ASCII 的來回轉換。
格式化小數與貨幣(SQLSRV) 使用 SQLSRV 驅動程式格式化 十進位金錢 欄位。
格式化小數和貨幣(PDO_SQLSRV) 使用 PDO_SQLSRV 驅動程式格式化 decimalmoney 欄位。
非系統區域設定 局部十進位分隔符及其他地區考量。

錯誤和診斷

文章 Description
處理錯誤與警告 兩個驅動程式都有錯誤與警告處理。
配置錯誤與警告處理(SQLSRV) 調整 SQLSRV 驅動程式如何回報錯誤與警告。
處理錯誤與警告(SQLSRV) 檢查 SQLSRV 函式所回傳的錯誤。
記錄活動 啟用驅動程式日誌以進行診斷擷取。

部署和操作

文章 Description
性能調校 連線管理、批次處理、預備語句、游標、記憶體,以及伺服器端監控。
Troubleshooting 診斷常見的安裝、連線、查詢、資料型別、交易及容器問題。

參考內容

文章 Description
SQLSRV 驅動程式 API 參考 所有 sqlsrv_* 函式、參數與回傳值。
PDO_SQLSRV 驅動程式參考 PDO_SQLSRV 驅動程式所支援的 PDO 和 PDOStatement 方法。
常數 驅動程式會暴露的常數,包括型別與編碼常數。
文章 Description
發行說明 各版本歷史,包含新功能、錯誤修正、平台支援變更及下載連結。
關於文件中的程式碼範例 本節範例程式碼所使用的慣例。
PHP SQL 驅動程式的範例 SQLSRV 與 PDO_SQLSRV 的端到端範例應用程式。
支援資源 社群與支援管道。