Microsoft PHP for SQL Server 驱动程序

下载 PHP 驱动程序

Microsoft PHP for SQL Server 驱动程序是 PHP 扩展,你可以用它通过 PHP 脚本读取和写入 Microsoft SQL 数据库引擎 中的数据。 这两个扩展使用相同的 Microsoft ODBC Driver for SQL Server,但它们提供了不同的 PHP API 和驱动程序专用选项:

  • SQLSRV 会暴露一个针对 SQL Server 特性定制的过程式 API(sqlsrv_*函数)。
  • PDO_SQLSRV 实现 了PHP数据对象(PDO) 接口。 你可以保留现有数据库代码中的PDO调用,但在切换数据库前查看其查询和驱动程序特定设置。

这两个驱动程序都连接到 Azure SQL 数据库、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 数据库 故障切换组和 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,使用 sqlsrv_configure() 配置 LogSeverity 和 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 应用服务或 Azure 容器实例上,使用该标识的客户端 ID;对于其他情况,使用其对象 ID。 PHP 驱动程序继承了底层 Microsoft ODBC 驱动 for SQL Server 的行为;更多信息请参见“使用 Microsoft Entra ID 配合 ODBC 驱动。 使用PDO构造函数的用户名参数,而不是将身份嵌入DSN。 如示例所示,将 null 作为用户传入,即可选用 Azure 主机的系统分配托管标识。 对于 SQLSRV(过程式),请在连接选项数组中传入 UID。

  • 当连接到故障转移组侦听器、可用性组侦听器或故障转移群集实例终结点时,设置 MultiSubnetFailover=true。 设置该值可提升单子网和多子网可用性组监听者的连接性能。 更多信息请参见 高可用性支持,灾难恢复。

  • 对于读取横向扩展或可读的辅助副本,可以将 ApplicationIntent=ReadOnly 添加到 DSN 中。

  • 使用你的SQL服务提供的端点,包括其云端专用域。 调查证书名称不匹配的问题,而不是禁用证书验证。 只有在知道该端点预期证书名称时才使用 HostNameInCertificate 。

  • ODBC 获取用于托管身份和服务主体认证的令牌。 使用 AccessToken时,你的应用获取令牌并将其传递给驱动程序;ODBC 不会帮你获取。 有关详细信息,请参阅在 ODBC 驱动程序中使用 Microsoft Entra ID。

  • 将连接信息放在你的代码之外。 把它存储在你应用的配置系统里,并用 Azure 密钥保管库 来管理机密。

  • 使用 SQLSRV,在传递给 sqlsrv_connect() 的数组中设置连接选项。 在决定是否重试前,先检查 false 返回值并检查 sqlsrv_errors()。 使用 QueryTimeout 单独设置语句超时。 空闲连接弹性 展示的是ODBC重新连接,而非应用层的重试循环。

  • 重试帮助程序会读取由 isset() 保护的 $e->errorInfo[1]。 PDOException::$errorInfo 被声明为 ?array,默认值为 null,因此防御性检查会回退到值为 0 的驱动程序代码,并由 SQLSTATE 的 08 前缀决定是否重试。

有关此配置的每个部分的详细信息,请参阅:

有关 Azure SQL 瞬态错误列表,请参阅排查瞬态连接错误。

主要功能

  • 两个 PHP API:SQLSRV 提供过程函数。 PDO_SQLSRV实现了PDO。 安装其中一个扩展或两个都安装。
  • 广泛支持平台:支持Windows、Linux和macOS,支持PHP版本。
  • 加密连接:通过 Encrypt=trueTLS 加密的连接,服务器证书验证由 TrustServerCertificate控制。
  • Microsoft Entra ID 认证:管理身份、服务主体及通过底层 Microsoft ODBC 驱动 for SQL Server 获得的访问令牌。 托管身份是无密码的; ActiveDirectoryServicePrincipal 使用客户端秘密。
  • Always Encrypted:在客户端加密敏感列,并通过安全隔离区支持对加密数据执行受支持的操作。
  • 连接恢复能力:内置空闲连接重试机制,支持 ConnectRetryCount 和 ConnectRetryInterval。
  • PHP 流:通过任一 API 从流参数发送大型二进制和字符值。 使用 SQLSRV 进行流式检索。
  • 丰富的 SQL Server 数据类型支持:datetimeoffset、表值参数、nvarchar,以及结合 PDO::SQLSRV_ENCODING_UTF8 的 Unicode。

开始

文章 Description
系统要求 支持PHP、操作系统和SQL Server版本。
支持矩阵 PHP驱动版本的详细兼容性矩阵。
下载用于 SQL Server 的 Microsoft PHP 驱动程序 下载链接和发布项目。
Linux 和 macOS 安装教程 在Linux和macOS上安装驱动及其ODBC前置条件。
加载驱动程序 在php.ini中启用扩展。
快速入门:连接并查询 Microsoft PHP 驱动程序 验证扩展,配置连接,并用 SQLSRV 或 PDO_SQLSRV 运行同样的参数化查询。
PHP SQL 驱动概述 包里有什么,什么时候该选择 SQLSRV 或 PDO_SQLSRV。

配置和连接

文章 Description
连接服务器 从PHP打开一个SQL Server实例的连接。
连接选项 关于连接关键词、默认值及其设置方法的完整参考资料。
连接到 Microsoft Azure SQL 数据库 将PHP应用连接到Azure SQL 数据库。
连接到指定的端口 选择非默认的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 通过安全 Enclave,对加密列启用丰富操作。

检索和更新数据

文章 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 存储库中创建议题。