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

下載 PHP 驅動程式

Microsoft PHP for SQL Server 驅動程式是 PHP 擴充功能,你可以用來從 PHP 腳本讀寫 Microsoft SQL 資料庫引擎 中的資料。 這兩個擴充功能使用相同的 Microsoft ODBC 驅動程式 for SQL Server,但它們提供不同的 PHP API 及驅動程式專屬選項:

  • SQLSRV 會暴露一個針對 SQL Server 功能量身打造的程序式 API(sqlsrv_*函式)。
  • PDO_SQLSRV 實作了 PHP 資料物件(PDO) 介面。 你可以保留 PDO 呼叫在現有資料庫程式碼中,但在切換資料庫前先檢視查詢和驅動程式的專屬設定。

兩個驅動程式都可連接 Azure SQL Database、Microsoft Fabric 中的 SQL 資料庫、Azure SQL 受控執行個體,以及所有支援的 SQL Server 版本和版本類型,包括 Express 版本。 兩個驅動程式都接受 PHP 串流作為輸入參數。 SQLSRV 支援以串流形式取得大型二進位與字元值。 不要以為 PDO_SQLSRV 有與其他項目相同的輸出串流行為。

選擇你的起點

Azure SQL 的生產環境基準

這個 PDO_SQLSRV 範例會從環境變數讀取伺服器和資料庫,並使用受控識別進行連線。 它建立資料來源名稱(DSN),並啟用傳輸層安全性(TLS)與憑證驗證。 請從受信任的應用程式設定讀取這些值,而非從網頁請求參數讀取。 在授與該身分對資料庫的存取權後,在擁有受管理身分端點的主機上執行該程式。

當出現列出的錯誤時,輔助函式最多嘗試三次連線或執行唯讀查詢。 他們在嘗試之間等待的時間變長,這是一種稱為 指數退縮的重試模式。 若查詢錯誤需要新連線,查詢助手會拋出異常,讓呼叫程式碼處理。

使用支援的 PHP 版本、PDO_SQLSRV 擴充功能,以及適用於 SQL Server 的 Microsoft ODBC Driver 18。 關於支援的認證值,請參見使用 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];

// This read helper retries only the listed errors on the same connection.
// Other errors propagate; the caller must decide whether to reconnect.
const RETRYABLE_READ_ERROR_CODES = [1205, 1222, 40501, 10928, 10929];

/**
 * 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);
            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');
}

/**
 * Retry the standalone read-only query for errors that allow connection reuse.
 */
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, RETRYABLE_READ_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.');
}
$databaseValue = '{' . str_replace('}', '}}', $database) . '}';

$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,
    $databaseValue
);

$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 name FROM sys.databases WHERE database_id > ? ORDER BY name', [1]);
foreach ($stmt as $row) {
    echo $row['name'], PHP_EOL;
}

請將這些設定用於 Azure SQL Database 故障轉移群組和 Azure SQL 受控執行個體。

  • Driver={ODBC Driver 18 for SQL Server} 明確選擇 ODBC 驅動程式 18,而非依賴驅動程式發現。 在執行範例前先安裝它。

  • 設定 SQL_DATABASE 為資料庫的實際名稱。 該程式將閉合大括號加倍,並在放入 DSN 前將名字包覆在大括號中。 跳脫可以避免名稱中的特殊字元改變驅動程式讀取連線設定的方式。

  • ConnectRetryCount 以及 ConnectRetryInterval 控制 ODBC 閒置連線彈性。 該語句的查詢逾時會界定執行期間的復原範圍。 由於範例中的查詢逾時為 30 秒,ODBC 可能會在用盡所有五次重新連線嘗試前停止。 LoginTimeout=90 控制的是連線建立,而不是那個恢復預算。 應用程式層級的輔助工具在發生特定伺服器錯誤時會重試;它不會重試查詢逾時或重播部分擷取的結果。

  • 啟用驅動程式記錄以及程式的 error_log() 呼叫。 對於 PDO_SQLSRV,可以在 php.ini 中設定 pdo_sqlsrv.log_severity,或在執行階段搭配 ini_set()。 對於 SQLSRV,請設定 LogSeverity 和 LogSubsystems 。sqlsrv_configure() 欲了解更多資訊,請參閱 日誌活動。

    ; 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 建構子的使用者名稱參數,而非將認證資訊嵌入 DSN。 假扮null使用者(如範例所示)選擇系統指派的 Azure 主機管理身份。 對於 SQLSRV(程序式),請在連線選項陣列中傳遞 UID。

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

  • 若要使用讀取向外延展或可讀取的次要複本,請將 ApplicationIntent=ReadOnly 新增至 DSN。

  • 使用你 SQL 服務提供的端點,包括其雲端專用網域。 調查憑證名稱不符的問題,而非停用憑證驗證。 只有在你知道該端點預期憑證名稱時才使用 HostNameInCertificate 。

  • ODBC 為管理身份及服務主體認證取得憑證。 有了 AccessToken,你的應用程式會取得該代幣並傳給驅動程式;ODBC 不會幫你取得。 如需詳細資訊,請參閱搭配使用 Microsoft Entra ID 與 ODBC 驅動程式。

  • 連線資訊要放在你的程式碼之外。 把它存進你應用程式的設定系統,並用 Azure Key Vault 來管理秘密。

  • 使用 SQLSRV,請在傳遞給 sqlsrv_connect() 的陣列中設定連線選項。 在決定是否重試前,請檢查退換 false 品並檢查 sqlsrv_errors() 。 使用 QueryTimeout 分別設定語句的逾時。 閒置連線彈性 展示的是 ODBC 重新連線,而非應用程式層級的重試迴圈。

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

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

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

重點功能

  • 兩個 PHP API:SQLSRV 提供程序式函式。 PDO_SQLSRV 實作 PDO。 安裝其中一個擴充功能或兩個都安裝。
  • 廣泛支援平台:可在 Windows、Linux 及 macOS 上運行,並支援 PHP 版本。
  • 加密連線:透過 Encrypt=true的 TLS 加密連線,伺服器憑證驗證由 TrustServerCertificate控制。
  • Microsoft Entra ID 認證:受控識別、服務主體,以及透過底層 Microsoft ODBC Driver for SQL Server 取得的存取權杖。 受控識別是無密碼的;ActiveDirectoryServicePrincipal 使用用戶端密碼。
  • Always Encrypted:在用戶端加密敏感欄位,並以安全隔區支援對加密資料執行支援的作業。
  • 連線韌性:內建使用 ConnectRetryCount 和 ConnectRetryInterval 的閒置連線重試機制。
  • PHP 串流:透過任一 API 從串流參數傳送大型二進位與字元值。 使用 SQLSRV 進行串流檢索。
  • 豐富的 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 中啟用擴充功能。
快速入門:連接並查詢 Microsoft PHP 驅動程式 驗證擴充功能,設定連線,並用 SQLSRV 或 PDO_SQLSRV 執行相同的參數化查詢。
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 使用整合驗證,或在 Linux 和 macOS 使用 Kerberos。

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 資料 設定 SQLSRV 的 CharacterSet 選項,或 PDO_SQLSRV 的 PDO::SQLSRV_ATTR_ENCODING 屬性。
在 Linux 和 macOS 上傳送與取回 ASCII 資料 在非 Windows 主機上處理 ASCII 的來回轉換。
格式化小數與貨幣(SQLSRV) 使用 SQLSRV 驅動程式格式化 十進位 和 金錢 欄位。
格式化小數和貨幣(PDO_SQLSRV) 使用 PDO_SQLSRV 驅動程式格式化 decimal 和 money 欄位。
非系統區域設定 局部十進位分隔符及其他地區考量。

錯誤和診斷

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

部署和操作

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

參考內容

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

提出功能要求

若要要求新增功能,請在 Microsoft Drivers for PHP for SQL Server GitHub 存放庫中提出 issue。