在Linux和macOS上安装Microsoft PHP驱动程序

这些说明将指导你安装 PHP、Microsoft ODBC Driver for SQL Server,以及 SQLSRV 和 PDO_SQLSRV 扩展。 每个标签页都使用该标签页从中安装的仓库提供的 PHP 包,因此命令中的版本在不同标签页之间会有所不同。 如果该仓库提供不同的版本,请将其替换为 支持矩阵 中列出的另一个 PHP 版本。

SQLSRV 和 PDO_SQLSRV 驱动使用相同的 ODBC 依赖,可以独立安装。 如果你的应用同时使用两个驱动,就安装两个扩展。

在过程结束时,CLI 和所选的 Web 服务器运行时应加载两个扩展,两个 PHP API 都应可用,且每个 API 的示例应返回数据库服务器版本。

开始之前

  1. 选择当前驱动版本支持的 PHP 版本,并可用于你的操作系统发行版。

  2. 决定你需要哪些PHP运行时:

    • 用于脚本、调度任务或工作程序的命令行界面。
    • 用于Apache托管PHP的Apache模块。
    • 用于 nginx 的 PHP-FPM 或 Apache FastCGI 配置。
  3. 确认操作系统架构。 已发布的Linux归档包含x64二进制文件,发布的macOS归档文件包含Arm64二进制文件。 PIE 适用于计算机上的 PHP 安装,是本文的主要安装路径。

  4. 安装 Microsoft ODBC 驱动和 unixODBC 开发包。 Microsoft 包负责数据库通信。 unixODBC 包提供了构建 SQLSRV 和 PDO_SQLSRV 所需的头文件。

  5. 计划在安装或更改扩展后重启所有 Web 服务器、PHP-FPM 池和长期运行的工作进程。

平台包管理器安装PHP和原生前置条件。 PIE负责构建和安装PHP扩展。 PHP 配置控制每个运行时是否加载它们。 ODBC 驱动在扩展加载后处理数据库通信和加密。

使用 PIE 安装驱动程序

PIE,即 PHP 扩展安装程序,是当前驱动版本中支持的扩展安装程序。 它取代了已被弃用的 PHP 扩展社区库(PECL) 工作流程。

请先完成以下各节中列出的平台先决条件,然后再安装驱动程序。 然后分别安装PIE和每个驱动:

安装PIE

PIE 运行于 PHP 8.1 及更高版本。 当你选择另一个PHP安装的开发工具时,它可以为该安装安装扩展。

请先安装 curl 和 GitHub CLI,然后再继续。 平台部分包括 curl,而最小安装可能不提供这些内容。

下载PIE PHP归档(PHAR),并在安装或运行前通过GitHub命令行界面(CLI)进行验证:

pie_tmp=$(mktemp -d)
curl -fL --output "$pie_tmp/pie.phar" https://github.com/php/pie/releases/latest/download/pie.phar
gh attestation verify --owner php "$pie_tmp/pie.phar"

只有在验证成功时才继续:

sudo install -m 755 "$pie_tmp/pie.phar" /usr/local/bin/pie
rm "$pie_tmp/pie.phar"
rmdir "$pie_tmp"
unset pie_tmp

临时目录是私有的,属于你的账户。 如果下载或验证失败,不要安装或运行PHAR。 在重试前先删除临时文件和目录。

确认已安装的 PHAR 可与你打算使用的 PHP 可执行文件一起运行:

php /usr/local/bin/pie --version

安装SQLSRV和PDO_SQLSRV驱动

在 Linux 和 macOS 上,PIE 会检查所需的编译器和 PHP 开发工具。 在运行 PIE 之前,先安装 ODBC 平台开发包和一个 C++ 编译器。 从交互式终端运行PIE。 当它需要权限将文件安装到系统目录时,可以要求你输入 sudo 密码。

PIE 以用于调用它的 PHP 安装为目标。 对于版本化安装,可以在每个安装命令中添加 --with-php-config 以选择该版本的开发工具。 例如,当预期工具为 pie install --with-php-config=/usr/bin/php-config8.4 microsoft/sqlsrv 时,使用 /usr/bin/php-config8.4。 将同样的选项应用到 microsoft/pdo_sqlsrv。 Alpine 使用版本化的可执行名; 请遵循 Alpine 命令 ,而非本节中未版本化的命令。

为每个扩展运行一条命令。 这些命令使用PIE默认的PHP安装:

pie install microsoft/sqlsrv
pie install microsoft/pdo_sqlsrv

PIE 尝试通过平台的 PHP 配置工具或条 extension= 目启用每个扩展,若无法启用则会警告。 PHP 为每个执行环境使用服务器应用程序接口(SAPI)。 在 Debian、Ubuntu 和 SUSE 上,命令行、Apache 和 PHP FastCGI Process Manager(PHP-FPM) 配置可能有所不同。 成功的命令行安装并不意味着 Web 服务器加载了驱动程序。

查看 php --ini 以获取命令行。 在为网页服务器创建临时 phpinfo() 页面之前,先 限制诊断页面的访问。 检查已加载的配置文件和每个运行时扫描的.ini文件。 如果缺少这些条目,请按照 在 Linux 和 macOS 上加载驱动程序 中的说明,以按正确的加载顺序添加它们。 配置更改后重启网页服务器或 PHP-FPM。 Red Hat 通常会在 SAPI 之间共享 php.ini ,但在依赖一次 PIE 运行前,请先验证当前的配置。

安装到你的平台上

选择您的操作系统和网页服务器配置。 完成该标签页的安装步骤,然后按照 “测试您的安装”操作。

在 Ubuntu 上安装

注释

以下命令使用 packages.sury.org PHP仓库,以便选择支持的PHP版本。 按照 Microsoft 官方说明安装适用于你的 Ubuntu 发行版的 ODBC 驱动程序。

步骤 1. 安装 PHP (Ubuntu)

sudo apt-get update
sudo apt-get install ca-certificates apt-transport-https curl gnupg2 lsb-release -y
sudo install -d -m 755 /etc/apt/keyrings
sudo curl -fsSL https://packages.sury.org/php/apt.gpg -o /etc/apt/keyrings/php.gpg
sudo chmod 644 /etc/apt/keyrings/php.gpg
echo "deb [signed-by=/etc/apt/keyrings/php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php.list
sudo apt-get update
sudo apt-get install php8.4 php8.4-dev php8.4-xml -y

步骤 2. 安装必备组件 (Ubuntu)

按照“在 Linux 上安装适用于 SQL Server 的 Microsoft ODBC 驱动程序”中的说明,然后安装开发头文件。

sudo apt-get install unixodbc-dev g++ make git -y

步骤 3. 安装带有 PIE 的 Microsoft SQL Server (Ubuntu) 的 PHP 驱动程序

按照“用 PIE 安装驱动”操作,并在两个安装命令中都添加 --with-php-config=/usr/bin/php-config8.4。 请使用 php8.4 --ini 检查命令行配置文件。

/usr/local/bin/pie install --with-php-config=/usr/bin/php-config8.4 microsoft/sqlsrv
/usr/local/bin/pie install --with-php-config=/usr/bin/php-config8.4 microsoft/pdo_sqlsrv

步骤 4. 安装 Apache 并配置驱动程序加载(Ubuntu)

sudo apt-get install apache2 libapache2-mod-php8.4 -y
sudo a2enmod php8.4

步骤 5. 重启 Apache 并测试示例脚本(Ubuntu)

sudo systemctl restart apache2

若要测试安装,请参阅本文档末尾 的“测试安装 ”。

旧版 PECL 安装

PECL 已弃用于当前驱动程序版本发布。 使用PIE进行新安装和升级。 如果已有部署被钉在 PECL,保持其包版本与 PHP 运行时保持一致,并计划迁移到 PIE。

测试你的安装

验证命令行运行时。 在 Alpine 上,将php替换为php84。 对于在其他发行版上的版本化 PHP 安装,可以使用其对应的可执行文件。

php --ri sqlsrv
php --ri pdo_sqlsrv
php -m | grep -E "sqlsrv|pdo_sqlsrv"

两个 --ri 命令都应打印包含 ExtensionVer的模块信息。 模块列表应包含单独的 sqlsrv 行和 pdo_sqlsrv 行。

在测试连接前,先使用 verify-php-drivers.php 验证两个 PHP API 接口:

<?php
$checks = [
    'SQLSRV' => extension_loaded('sqlsrv') && function_exists('sqlsrv_connect'),
    'PDO_SQLSRV' => extension_loaded('pdo_sqlsrv')
        && in_array('sqlsrv', PDO::getAvailableDrivers(), true),
];

$failed = false;
foreach ($checks as $name => $loaded) {
    echo "$name: " . ($loaded ? 'loaded' : 'missing') . PHP_EOL;
    $failed = $failed || !$loaded;
}

exit($failed ? 1 : 0);

预期输出:

SQLSRV: loaded
PDO_SQLSRV: loaded

对于 PHP-FPM 或网页服务器,在创建临时页面前先phpinfo()。 确认 已加载的配置文件、 extension_dir和两个驱动部分。 更改配置或更换扩展后,重启 PHP-FPM 或网页服务器。 验证后删除页面。

要测试实时连接,可以在系统文档根目录下创建一个名为 testsql.php 的文件。 该路径在 Ubuntu、Debian 和 Red Hat 上是 /var/www/html,在 SUSE 上是 /srv/www/htdocs,在 Alpine 上是 /var/www/localhost/htdocs,而在 macOS 上是 $(brew --prefix)/var/www。 在运行脚本之前,先为PHP进程设置 SQL_SERVER_NAME、 SQL_DATABASE、 SQL_UID和 SQL_PWD 环境变量。 不要在文档根目录下把凭证硬编码在文件里。

对于命令行测试,使用 交互式提示 设置环境变量,无需在shell历史中输入密码。

对于 Apache 模块测试,请勿将凭证放入网站通常可读的配置文件中:

  1. 在文档根节点外创建一个私有文件。 使用 Apache 配置目录中未使用的文件名。 在 Linux 上:

    sudo install -o root -g root -m 600 /dev/null /etc/apache2/php-sql-driver-test.conf
    

    在 Red Hat 上,将 /etc/apache2 替换为 /etc/httpd。 在 macOS 上,使用启动 Homebrew Apache 的账户创建该文件:

    install -m 600 /dev/null "$(brew --prefix)/etc/httpd/php-sql-driver-test.conf"
    

    只有文件所有者和 root 用户能读取这些设置。 启动Apache的账户必须能够读取该文件。

  2. 在文本编辑器中编辑私有文件,添加以下条目:

    SetEnv SQL_SERVER_NAME "tcp:<server>,1433"
    SetEnv SQL_DATABASE "<database>"
    SetEnv SQL_UID "<user_id>"
    SetEnv SQL_PWD "<password>"
    

    在双引号参数中,每个字面反斜杠写作 \",每个字面双引号写作 \\。 在引号内保留前置和后置空格。 Apache也用 ${NAME} 来替代引用。 如果某个值包含该序列或换行,则使用命令行测试。 更多信息请参见 Apache 配置引用和转义。

  3. 在网站的虚拟主机内,添加一个 Files 块,仅为 testsql.php 包含私有文件。 如果没有配置虚拟主机,则将该块置入主服务器配置。 用私有文件的绝对路径替换占位符:

    <Files "testsql.php">
        Include "<absolute-path-to-private-file>"
    </Files>
    
  4. 重启Apache。 测试后,移除你添加的 Files 块,删除私有文件,然后重新启动Apache。 保留无关站点设置。

PHP-FPM 默认清除继承的环境变量,nginx不会将其环境传递给PHP-FPM。 仅在隔离的测试 PHP-FPM 服务和池中使用以下 Ubuntu 或 Red Hat 流程。 不要把测试凭据添加到运行其他应用的服务或池里:该池中的每个脚本都能读取它们。 如果你用的是共享主机,建议用命令行测试。

  1. 在文档根目录外创建一个root拥有的文件,例如 /etc/php-sql-driver-test.env,并限制访问:

    sudo touch /etc/php-sql-driver-test.env
    sudo chmod 600 /etc/php-sql-driver-test.env
    

    将连接设置添加到文件中:

    SQL_SERVER_NAME="tcp:<server>,1433"
    SQL_DATABASE="<database>"
    SQL_UID="<user_id>"
    SQL_PWD="<password>"
    

    每个设置都各占一行。 在双引号中,对于每个字面反斜杠写 \\,对于每个字面双引号写 \"。 美元符号和反引号不会被解释为 shell 表达式。 直接编辑文件,而不是在 shell 中运行它或执行 source 命令加载它。 更多信息请参见 systemd EnvironmentFile 语法。

  2. 可以在 Ubuntu 上运行sudo systemctl edit php-fpm,或在sudo systemctl edit php8.4-fpm Red Hat 上运行。 添加这个 systemd 覆盖配置:

    [Service]
    EnvironmentFile=/etc/php-sql-driver-test.env
    
  3. 将这些条目添加到活跃 PHP-FPM 池配置中。 常见的位置包括 Ubuntu 上的 /etc/php/8.4/fpm/pool.d/www.conf 和 Red Hat 上的 /etc/php-fpm.d/www.conf:

    env[SQL_SERVER_NAME] = $SQL_SERVER_NAME
    env[SQL_DATABASE] = $SQL_DATABASE
    env[SQL_UID] = $SQL_UID
    env[SQL_PWD] = $SQL_PWD
    
  4. 重新加载systemd并重启适用的 PHP-FPM 服务:

    sudo systemctl daemon-reload
    sudo systemctl restart php8.4-fpm
    

    在 Red Hat 上,将 php8.4-fpm 替换为 php-fpm。

测试后,删除临时环境文件、你添加的 EnvironmentFile= 条目、测试池条目和 testsql.php。 只有当服务覆盖文件是为本次测试创建且没有其他设置时,才删除服务覆盖文件。 保留所有已有的覆盖项和池设置。 删除测试配置后,重新加载systemd并再次重启 PHP-FPM。

SQLSRV 示例

保持环境值不变。 每个示例在连接前都会检查并拒绝缺失或空的设置。 每个示例随后将右大括号加倍并添加外层大括号,然后将数据库名称、用户名和密码传递给驱动程序。 这种编码保留了字面大括号,并防止分号分隔连接选项。

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

if ($serverName === false || $serverName === ''
    || $databaseName === false || $databaseName === ''
    || $uid === false || $uid === ''
    || $pwd === false || $pwd === '') {
    handle_failure("Set SQL_SERVER_NAME, SQL_DATABASE, SQL_UID, and SQL_PWD.");
}

$databaseName = '{' . str_replace('}', '}}', $databaseName) . '}';
$uid = '{' . str_replace('}', '}}', $uid) . '}';
$pwd = '{' . str_replace('}', '}}', $pwd) . '}';
$connectionOptions = array(
    "Database" => $databaseName,
    "UID" => $uid,
    "PWD" => $pwd,
    "Encrypt" => 1,
    "TrustServerCertificate" => 0
);

function handle_failure($message) {
    error_log($message);
    http_response_code(500);
    echo "<h1>Failure</h1>";
    echo "An error occurred. Check the server error log for details.";
    exit(1);
}

function format_errors($errors) {
    $lines = [];
    foreach ($errors as $error) {
        $lines[] = "SQLSTATE: {$error['SQLSTATE']}, code: {$error['code']}, message: {$error['message']}";
    }
    return implode(PHP_EOL, $lines);
}

$conn = sqlsrv_connect($serverName, $connectionOptions);
if ($conn === false) {
    handle_failure(format_errors(sqlsrv_errors()));
}

$tsql = "SELECT @@Version AS SQL_VERSION";

$stmt = sqlsrv_query($conn, $tsql);
if ($stmt === false) {
    handle_failure(format_errors(sqlsrv_errors()));
}

$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if ($row === null || $row === false) {
    handle_failure("Query returned no rows.");
}

echo "<h1>Success</h1>";
echo htmlspecialchars($row['SQL_VERSION']);

sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
?>

PDO_SQLSRV 示例

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

if ($serverName === false || $serverName === ''
    || $databaseName === false || $databaseName === ''
    || $uid === false || $uid === ''
    || $pwd === false || $pwd === '') {
    handle_failure(new RuntimeException("Set SQL_SERVER_NAME, SQL_DATABASE, SQL_UID, and SQL_PWD."));
}

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

function handle_failure($exception) {
    error_log($exception->getMessage());
    http_response_code(500);
    echo "<h1>Failure</h1>";
    echo "An error occurred. Check the server error log for details.";
    exit(1);
}

try {
    $conn = new PDO(
        "sqlsrv:server=$serverName;Database=$databaseName;Encrypt=1;TrustServerCertificate=0",
        $uid,
        $pwd
    );
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $tsql = "SELECT @@Version AS SQL_VERSION";

    $stmt = $conn->query($tsql);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    if ($row === false) {
        throw new RuntimeException("Query returned no rows.");
    }

    echo "<h1>Success</h1>";
    echo htmlspecialchars($row['SQL_VERSION']);
} catch (Throwable $exception) {
    handle_failure($exception);
}

unset($stmt);
unset($conn);
?>

将浏览器指向 http://localhost/testsql.php (http://localhost:8080/testsql.php 在 macOS 上)。 连接成功时,数据库服务器的版本字符串会显示。 这两个示例都会将错误详细信息写入 PHP 错误日志。 如果页面显示失败,请查看该日志。

要比较命令行和网页服务器的行为,请为终端设置相同的环境变量并运行:

php testsql.php

如果命令行运行成功但浏览器中没有任何提示,请检查 Apache日志文件。 如需更多帮助,请参见 PHP SQL 驱动的支持资源。

分别评估SQL Server、Azure SQL 数据库、Azure SQL 托管实例、Azure Synapse Analytics、Microsoft Fabric中的SQL数据库和Microsoft Fabric中的Warehouse的连接程序。 所选的认证方法、连接选项和查询必须得到目标的支持。 安装在客户端进行。 成功的扩展检查并不能证明目标是否接受凭证、信任客户端或支持所有数据库功能。

排查安装问题

问题 所属图层 纠正措施
PIE 找不到 phpize、php-config 或标头 PHP 开发包 安装目标PHP版本的开发包,并传递匹配 --with-php-config 选项
编译失败是因为缺少一个 C++ 编译器 操作系统构建工具 安装平台的C++编译器包,例如 g++ 或 gcc-c++
编译找不到 sql.h ODBC 开发包或编译器路径 安装 unixodbc-dev 或 unixODBC-devel;在 Apple 芯片上,设置 Homebrew include 和库标志
php --ri 提示没有扩展功能 PHP 配置 检查已加载的php.ini和扫描目录,添加正确的条目,然后重新开始流程
PDO报告 could not find driver PDO_SQLSRV 加载或 PDO 加载顺序 先确认 PDO 加载成功,PDO_SQLSRV 使用后续扫描的配置文件
CLI 能用,但 Apache、nginx 或 PHP-FPM 失败 SAPI 专用配置 将CLI输出与 phpinfo()对比,配置网页SAPI,并重启 web SAPI
nginx 向浏览器返回 HTTP 502 nginx 到 PHP-FPM 的路由配置 检查 fastcgi_pass 套接字是否匹配安装的 PHP-FPM 版本,确认服务是否正常运行
浏览器示例报告环境值缺失 网页服务器或 PHP-FPM 环境 配置所选Web运行时的变量,然后重启它
扩展程序已加载,但连接失败 ODBC、网络、证书、认证或SQL产品 读取服务器日志中的 SQLSTATE 和本地错误;除非错误表明存在加载或二进制兼容性问题,否则不要重建扩展

移除临时测试配置

验证后:

  1. 删除文档根目录中的 verify-php-drivers.php、testsql.php 以及任何临时 phpinfo() 页面。

  2. 移除临时凭证和环境转发条目。

  3. 更改 systemd 覆盖后重新加载服务管理器,然后重启 PHP-FPM 或 Web 服务器。

  4. 保留应用所需的扩展条目和已安装的包。