在 Windows 上安装 SQL Server 的 PHP 驱动程序

使用本文在 Windows 上为 PHP 安装 SQLSRV 和 PDO_SQLSRV 驱动程序。 驱动程序是使用 Microsoft ODBC Driver for SQL Server 的 PHP 扩展。

开始前,请确认你的PHP版本、架构和线程安全模式是否在 支持矩阵中得到支持。 命令行进程使用的PHP运行时和网页服务器使用的运行时可以使用不同的 php.ini 文件。 分别配置和验证每个运行时。

在过程结束时,sqlsrv_connect() 和 PDO sqlsrv 驱动应同时提供给目标 CLI 或 Web 应用,每个 API 应完成一次测试查询。 仅仅成功复制文件并不意味着安装完成。

选择安装方法

Method 在以下情况下使用 安装内容
PHP 扩展安装程序(PIE) 你希望安装程序为PHP可执行文件选择驱动构建 未进行版本控制的 php_sqlsrv.dll 和 php_pdo_sqlsrv.dll,尝试自动配置
已发布的 ZIP 压缩包 你需要自己选择、检查或部署精确的预建文件 按 PHP 小版本、线程安全模式和架构选择的带版本号的 DLL

两个扩展都用同一种方法。 不要同时保持PIE和ZIP配置条目活跃。

安装必备条件

  1. 从PHP for Windows网站安装受支持的 PHP 版本。 将 PHP 目录添加到 PATH。

  2. 安装你PHP构建所需的Visual C++再分发文件。 将其x64或x86架构与PHP匹配,如Windows PHP要求中所述。

  3. 安装最新的 Microsoft ODBC Driver for SQL Server。

  4. 执行以下命令以确认PHP运行时和配置文件:

    php --version
    php --ini
    

    如果 加载的配置文件 是 (none),则在 PHP 目录中将 php.ini-development 复制到 php.ini。

  5. 在 php.ini中,设置扩展目录并启用 PHP 扩展安装程序(PIE)所需的扩展:

    extension_dir = "ext"
    extension=openssl
    extension=zip
    
  6. 确认 PHP 能够定位必需扩展:

    php --ri openssl
    php --ri zip
    

    在下载 PIE 之前,先满足这些前提条件。

通过 PIE 安装

PIE 安装与 PHP 运行时匹配的预编译扩展。 在Windows上不需要C编译器。

  1. 将目录改为可写入的目录,并下载最新的PIE PHP归档(PHAR):

    curl.exe -fL --output pie.phar https://github.com/php/pie/releases/latest/download/pie.phar
    

    其余的 PIE 命令将该文件称为 .\pie.phar,因此请从同一目录运行它们。

    运行 PHAR 之前,请用GitHub 命令行工具(CLI)验证:

    gh attestation verify --owner php .\pie.phar
    

    继续前请确认下载内容:

    dir pie.phar
    php .\pie.phar --version
    
  2. 分别安装每个驱动程序:

    php .\pie.phar install microsoft/sqlsrv
    php .\pie.phar install microsoft/pdo_sqlsrv
    
  3. 验证命令行运行时:

    php --ri sqlsrv
    php --ri pdo_sqlsrv
    php -m | findstr /I "sqlsrv pdo_sqlsrv"
    

PIE 尝试在命令行可执行文件所使用的配置中启用扩展。 如果 PIE 报告无法启用扩展,则添加 针对 PIE 的扩展条目。

如果你的网页服务器使用了另一个 PHP 安装,在配置之前,先安装与该安装的 PHP 版本、架构和线程安全模式相匹配的两个驱动程序。 如果同一个 PHP 安装为 Web 服务器使用不同的 php.ini,请在该文件中启用已安装的驱动程序。 分别验证 Web 运行时,并在更改配置后重启 Web 服务器。

PIE 会更改所选的 PHP 安装实例。 它不会配置 IIS,不会选择 IIS 站点,也不会重启现有的 PHP 进程。

手动安装已发布的ZIP包

当你需要自己选择驱动文件,或者PIE不适用时,可以用这个方法。

  1. 请从Microsoft Drivers for PHP for SQL Server 发布页面下载已发布驱动版本的 Windows ZIP 文件。

  2. 运行 php -i | findstr /I "PHP Version Thread Safety Architecture extension_dir" 并记录 PHP 版本、线程安全模式、架构和扩展目录。

  3. 从ZIP文件中,将两个匹配的动态链接库(DLL)复制到扩展目录:

    php_sqlsrv_<php-version>_<ts-or-nts>_<x86-or-x64>.dll
    php_pdo_sqlsrv_<php-version>_<ts-or-nts>_<x86-or-x64>.dll
    

    例如,PHP 8.5 x64 线程安全运行时使用 php_sqlsrv_85_ts_x64.dll 和 php_pdo_sqlsrv_85_ts_x64.dll。

  4. 将匹配的扩展名添加到运行时使用的 php.ini 中:

    extension=php_sqlsrv_85_ts_x64.dll
    extension=php_pdo_sqlsrv_85_ts_x64.dll
    

    用你的 PHP 运行时的值替换 85、ts 和 x64。 不要混用线程安全和非线程安全文件、32位和64位文件,或者为另一个 PHP 次版本构建的文件。

  5. 使用 php --ri sqlsrv、php --ri pdo_sqlsrv 和 php -m 验证运行时。 对于网页服务器,在创建临时页面前先phpinfo()。 验证 已加载的配置文件 和两个驱动程序部分,然后删除该页面。

配置一个 Web 服务器

对于 IIS,按照php-cgi.exe将目标 配置为 FastCGI 应用。 确认FastCGI应用使用了你安装驱动时相同的PHP目录和架构。

对于任何Windows Web服务器:

  1. 在创建临时页面之前,先phpinfo()。 识别网页服务器的 PHP 可执行文件和当前的 php.ini。

  2. 确保两个驱动都已安装用于网页运行时。 对于单独的PHP安装,使用PIE配合该安装的PHP可执行文件,或者将两个匹配的DLL从发布的ZIP复制到其扩展目录。 将驱动程序条目添加到网页运行时的配置中,包括其仅 php.ini 与 CLI 不同时的情况。

  3. 重启网页服务器或FastCGI进程。

  4. 确认页面包含独立的 sqlsrv 和 pdo_sqlsrv 章节。

  5. 删除 phpinfo() 页面。

测试两个PHP API

将此程序保存为 verify-php-drivers.php:

<?php
$failures = [];

if (!extension_loaded('sqlsrv') || !function_exists('sqlsrv_connect')) {
    $failures[] = 'SQLSRV is not available.';
}

if (!extension_loaded('pdo_sqlsrv') || !in_array('sqlsrv', PDO::getAvailableDrivers(), true)) {
    $failures[] = 'PDO_SQLSRV is not available.';
}

echo $failures === []
    ? "SQLSRV and PDO_SQLSRV are available." . PHP_EOL
    : implode(PHP_EOL, $failures) . PHP_EOL;

exit($failures === [] ? 0 : 1);

先从命令行运行它:

php verify-php-drivers.php

预期输出:

SQLSRV and PDO_SQLSRV are available.

如果你需要验证网页运行时,可以暂时将文件放在网站的文档根目录中,并在浏览器中请求该文件。 测试后删除。 该程序验证 PHP API,无需提供也不会暴露数据库凭证。

测试数据库连接

扩展加载只是安装时的第一项检查。 测试这两个API针对应用程序使用的数据库目标,因此测试还涵盖ODBC驱动、网络路径、加密、认证、查询执行、返回值和清理。

通过回答提示设置PowerShell中的临时环境变量。 输入服务器和数据库名称作为原始值,不加引号或驱动编码。 使用凭据对话框或提示,为可连接到数据库的测试账户输入凭据。 不要在命令行或录制的终端会话中包含凭据:

$env:SQL_SERVER_NAME = Read-Host "Server name (tcp:<server>,1433)"
$env:SQL_DATABASE = Read-Host "Database name"
$credential = Get-Credential -Message "Enter the database test account"
if ($null -eq $credential) {
    throw "Database test credentials are required."
}
$env:SQL_UID = $credential.UserName
$env:SQL_PWD = $credential.GetNetworkCredential().Password
Remove-Variable credential

凭据提示会隐藏密码。 环境包含明文密码,PHP 可以读取它。 这些变量只在测试会话中使用,不要打印它们,测试后清除它们。

将该程序保存为 test-php-drivers.php。 保持环境值不变。 该程序会将右花括号加倍并添加外层花括号,然后将数据库名称、用户名和密码传递给驱动程序。 这种编码保留了字面大括号,并防止分号分隔连接选项。

<?php
$serverName = getenv('SQL_SERVER_NAME');
$database = getenv('SQL_DATABASE');
$uid = getenv('SQL_UID');
$pwd = getenv('SQL_PWD');

if ($serverName === false || $serverName === ''
    || $database === false || $database === ''
    || $uid === false || $uid === ''
    || $pwd === false || $pwd === '') {
    fwrite(STDERR, "Set SQL_SERVER_NAME, SQL_DATABASE, SQL_UID, and SQL_PWD." . PHP_EOL);
    exit(1);
}

$database = '{' . str_replace('}', '}}', $database) . '}';
$uid = '{' . str_replace('}', '}}', $uid) . '}';
$pwd = '{' . str_replace('}', '}}', $pwd) . '}';

$connectionOptions = [
    "Database" => $database,
    "UID" => $uid,
    "PWD" => $pwd,
    "Encrypt" => 1,
    "TrustServerCertificate" => 0,
];

$sqlsrvConnection = sqlsrv_connect($serverName, $connectionOptions);
if ($sqlsrvConnection === false) {
    foreach (sqlsrv_errors() as $error) {
        fwrite(
            STDERR,
            "SQLSRV {$error['SQLSTATE']} ({$error['code']}): {$error['message']}" . PHP_EOL
        );
    }
    exit(1);
}

$sqlsrvStatement = sqlsrv_query($sqlsrvConnection, "SELECT 1 AS TEST_VALUE");
if ($sqlsrvStatement === false) {
    foreach (sqlsrv_errors() as $error) {
        fwrite(
            STDERR,
            "SQLSRV {$error['SQLSTATE']} ({$error['code']}): {$error['message']}" . PHP_EOL
        );
    }
    sqlsrv_close($sqlsrvConnection);
    exit(1);
}

$sqlsrvRow = sqlsrv_fetch_array($sqlsrvStatement, SQLSRV_FETCH_ASSOC);
if ($sqlsrvRow === false || $sqlsrvRow === null || (int) $sqlsrvRow['TEST_VALUE'] !== 1) {
    fwrite(STDERR, "SQLSRV returned an unexpected result." . PHP_EOL);
    sqlsrv_free_stmt($sqlsrvStatement);
    sqlsrv_close($sqlsrvConnection);
    exit(1);
}

echo "SQLSRV query: 1" . PHP_EOL;
sqlsrv_free_stmt($sqlsrvStatement);
sqlsrv_close($sqlsrvConnection);

$failed = false;
try {
    $pdoConnection = new PDO(
        "sqlsrv:server=$serverName;Database=$database;Encrypt=1;TrustServerCertificate=0",
        $uid,
        $pwd,
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
    );
    $pdoStatement = $pdoConnection->query("SELECT 1 AS TEST_VALUE");
    $pdoRow = $pdoStatement->fetch(PDO::FETCH_ASSOC);
    if ($pdoRow === false || (int) $pdoRow['TEST_VALUE'] !== 1) {
        throw new RuntimeException("PDO_SQLSRV returned an unexpected result.");
    }

    echo "PDO_SQLSRV query: 1" . PHP_EOL;
} catch (Throwable $exception) {
    fwrite(STDERR, $exception->getMessage() . PHP_EOL);
    $failed = true;
} finally {
    unset($pdoStatement);
    unset($pdoConnection);
}

exit($failed ? 1 : 0);

用你验证过的同一个PHP可执行文件运行该程序:

php test-php-drivers.php

预期输出:

SQLSRV query: 1
PDO_SQLSRV query: 1

如果扩展检查通过但此程序失败,请使用 SQLSTATE 和本机错误来识别其所属层。 不要因为网络、证书、认证或数据库权限错误而重新安装PHP扩展。

测试后移除脚本和临时变量:

Remove-Item .\verify-php-drivers.php, .\test-php-drivers.php
Remove-Item Env:\SQL_SERVER_NAME, Env:\SQL_DATABASE, Env:\SQL_UID, Env:\SQL_PWD

Troubleshooting

问题 需要检查的事项 修复
Could not open input file: .\pie.phar 当前目录及下载地点 返回到包含已验证 PHAR 的目录,或再次下载并再次验证它
Unable to load dynamic library extension_dir,确切的 DLL 名称、PHP 小版本、架构和线程安全模式 选择匹配的 DLL 或用预期的 PHP 可执行文件重跑 PIE
The specified module could not be found 驱动程序 DLL 依赖项 确认扩展 DLL 的存在,安装匹配的 Visual C++ Redistributable,并检查任何剩余缺失的 DLL 依赖
CLI 会加载两个驱动,但 IIS 不会 不同的PHP可执行文件或 php.ini 将php --ini与phpinfo()比较,配置 IIS 运行时,并重启 FastCGI
Module "sqlsrv" is already loaded 无论是PIE还是ZIP条目,或重复条目 保留一个SQLSRV条目和一个PDO_SQLSRV条目
PDO报告 could not find driver PDO_SQLSRV配置 运行 php --ri pdo_sqlsrv 并检查 PDO::getAvailableDrivers()
PIE报告成功,但旧版本仍然存在 版本化的 ZIP 条目仍处于活动状态 用PIE未带版本的DLL条替换旧条目,并重启所有PHP进程

如果两个扩展都能加载,PHP 可以调用它们的 API。 在更改驱动程序文件之前,捕捉 SQLSRV 错误或 PDO 异常。 重装PHP扩展并不能解决网络、认证、加密、证书和数据库权限错误。