支持 LocalDB

下载 PHP 驱动程序

SQL Server Express LocalDB 是 SQL Server Express 的一种轻量级用户实例模式,仅支持 Windows。 两个驱动程序连接到 LocalDB 实例的方式,与连接任何命名实例的方式相同,都是使用 (localdb)\<instance-name> 服务器名称格式。

备注

LocalDB 运行在 Windows 用户账户下。 这些程序使用Windows集成认证,而非Microsoft Entra认证。 当每个用户的数据库实例适合你的工作流程时,可以用它进行本地 Windows 开发。 它不是容器镜像,也不是 Azure 服务。

Note

Linux和macOS上都没有LocalDB。 改为连接到受支持主机上的 SQL Server 实例。 对于容器,请查看受支持的 Linux 平台上的 SQL Server;不要假设每个 macOS 或 Linux 体系结构都能运行 SQL Server 容器映像。

连接到 LocalDB 实例

安装一个支持的 SQL Server Express LocalDB 版本,以及你选择的 ODBC 前置条件和 PHP 扩展。 LocalDB 安装未加载 PHP 扩展。

  1. 在 Windows 终端里运行 sqllocaldb.exe i,查看你的实例列表。
  2. 如果 MSSQLLocalDB 不存在,则运行 sqllocaldb.exe create MSSQLLocalDB。
  3. 运行 sqllocaldb.exe start MSSQLLocalDB,然后运行 sqllocaldb.exe info MSSQLLocalDB 以检查该实例。 以运行PHP程序的Windows账户执行这些步骤。
  4. 保存并使用命令行 PHP 运行其中一个程序。 每个实例默认连接到 master 并验证目标是否真的是 LocalDB。 不需要示例数据库或SQL密码。

实例名称仅对该 Windows 计算机及其所属账户有效。 它不是 DNS 主机名、TCP 端口,也不是与另一台计算机的连接。

SQLSRV

将该程序保存为 connect-localdb-sqlsrv.php,并运行 php connect-localdb-sqlsrv.php。

<?php
$instance = getenv('LOCALDB_INSTANCE') ?: 'MSSQLLocalDB';
$database = getenv('LOCALDB_DATABASE');
if ($database === false || $database === '') {
    $database = 'master';
}
$file = getenv('LOCALDB_MDF');
$databaseValue = '{' . str_replace('}', '}}', $database) . '}';
$options = ['Database' => $databaseValue, 'LoginTimeout' => 30, 'CharacterSet' => 'UTF-8'];
if ($file !== false && $file !== '') {
    if ($database === 'master' || !is_file($file)) {
        throw new RuntimeException('Set a non-master database name and an accessible MDF path.');
    }
    $options['AttachDBFileName'] = '{' . str_replace('}', '}}', $file) . '}';
}
$conn = sqlsrv_connect('(localdb)\\' . $instance, $options);
if ($conn === false) {
    throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
$stmt = null;
try {
    $stmt = sqlsrv_query(
        $conn,
        "SELECT DB_NAME() AS DatabaseName, CAST(SERVERPROPERTY('IsLocalDB') AS int) AS IsLocalDB"
    );
    if ($stmt === false) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
    if ($row === false) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    if ($row !== ['DatabaseName' => $database, 'IsLocalDB' => 1]) {
        throw new RuntimeException('Expected the selected LocalDB database.');
    }
    echo 'LocalDB database: ', $row['DatabaseName'], PHP_EOL;
} finally {
    if (is_resource($stmt)) {
        sqlsrv_free_stmt($stmt);
    }
    sqlsrv_close($conn);
}

PDO_SQLSRV

将这个独立程序保存为 connect-localdb-pdo.php,并运行 php connect-localdb-pdo.php。

<?php
$instance = getenv('LOCALDB_INSTANCE') ?: 'MSSQLLocalDB';
$database = getenv('LOCALDB_DATABASE');
if ($database === false || $database === '') {
    $database = 'master';
}
$file = getenv('LOCALDB_MDF');
$databaseValue = '{' . str_replace('}', '}}', $database) . '}';
$dsn = "sqlsrv:Server=(localdb)\\$instance;Database=$databaseValue;LoginTimeout=30";
if ($file !== false && $file !== '') {
    if ($database === 'master' || !is_file($file)) {
        throw new RuntimeException('Set a non-master database name and an accessible MDF path.');
    }
    $dsn .= ';AttachDBFileName={' . str_replace('}', '}}', $file) . '}';
}
$pdo = new PDO($dsn, null, null, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE => true,
    PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_UTF8,
]);
$stmt = null;
try {
    $stmt = $pdo->query(
        "SELECT DB_NAME() AS DatabaseName, CAST(SERVERPROPERTY('IsLocalDB') AS int) AS IsLocalDB"
    );
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    if ($row !== ['DatabaseName' => $database, 'IsLocalDB' => 1]) {
        throw new RuntimeException('Expected the selected LocalDB database.');
    }
    echo 'LocalDB database: ', $row['DatabaseName'], PHP_EOL;
} finally {
    $stmt = null;
    $pdo = null;
}

默认配置下的预期输出:

LocalDB database: master

SQLSRV 会返回一个连接资源,并将连接失败报告为 false。 PDO 会返回一个对象,如果构造失败,则会抛出 PDOException。 PDO 程序显式启用数值获取,因此 int 值是 PHP int,与 SQLSRV 的结果一致。 两者都对字符数据使用 UTF-8,并且即使查询失败,也会释放语句和连接句柄。

选择现有数据库或附加文件

程序接受以下可选的过程变量:

变量 Meaning
LOCALDB_INSTANCE 不带 (localdb)\ 前缀的实例名。 默认值为 MSSQLLocalDB.
LOCALDB_DATABASE 现有数据库名称,或要为附加数据库指定的名称。 默认值为 master.
LOCALDB_MDF 要附加的现有且可访问的 .mdf 文件的绝对路径。 连接已注册数据库时省略它。

要使用已在该实例中注册的数据库,请设置 LOCALDB_DATABASE,并保持 LOCALDB_MDF 未设置。 要附加现有数据库文件,请同时设置这两项。 例如,在 PowerShell 中:

$env:LOCALDB_DATABASE = 'myData'
$env:LOCALDB_MDF = 'C:\Data\myData.mdf'

使用为此目的预编译数据库文件,而不是仍附加到另一个正在运行的实例的文件。 授予Windows账户数据库文件及其目录的访问权限。 AttachDBFileName 不会创建缺失的MDF文件、迁移数据库或解决不兼容的数据库文件版本。 附加操作会将数据库注册到该实例;关闭 PHP 并不会分离或删除它。 如果你需要分别验证两个 API,请从分离状态测试每个 API 的附加路径。

服务器字符串的反斜线在PHP单引号字符串中转义为 \\。 PHP 双引号字符串还会解释反斜杠和变量,因此 PDO 程序会有意构造实例字符串。 这两个程序都会在连接时对数据库名称进行转义,同时保留原始名称以用于验证和结果检查。 它们还会在转义附加操作的值之前,先检查实际的文件系统路径。 对于每个连接值,它们都会将右花括号写成两个,并用花括号将该值括起来。 SQLSRV 要求即使是在其连接数组中提供的字符串选项,也必须进行这种转义。 将 LOCALDB_DATABASE 和 LOCALDB_MDF 设置为实际名称和路径,不要自行添加或双写花括号。

这些连接使用本地实例,而非远程服务器。 对于远程SQL Server、Azure SQL 数据库、Azure SQL 托管实例、Azure Synapse Analytics、Fabric中的SQL数据库或Fabric中的Warehouse,使用实际的服务端点。 明确设置认证、加密和证书验证。 更多信息请参见“连接服务器”。

诊断账户和实例问题

你也可以用命令行通过 sqlcmd 管理 LocalDB 实例的数据库,例如 sqlcmd -S "(localdb)\MSSQLLocalDB"。 你的交互式 Windows 帐户所拥有的 LocalDB 实例,不会自动授予 Internet Information Services (IIS) 应用程序池标识访问权限。 更改运行PHP的账户时,检查实例所有权和文件权限。 更多信息请参见 SQL Server Express LocalDB。

症状 检查
sqllocaldb.exe 未找到 确认 LocalDB 已安装,且其工具可在终端中使用。 仅仅安装PHP驱动是不够的。
该实例未被发现 以运行 PHP 的同一 Windows 帐户列出实例,并检查 LOCALDB_INSTANCE。
初始连接时间太长 明确启动实例,并使用文档中记录的登录超时时间重试。 调查实例启动失败,而不是无限期重试。
master 能用,但 myData 不行 确认该实例中数据库是否存在,或者配置一个可访问的附件文件和数据库名称。
文件附加失败 检查准确路径、文件所有权、目录权限、文件是否已被使用以及 LocalDB 版本兼容性。
终端PHP能用,但IIS不行 比较账户、实例所有权、文件访问、运行时配置和扩展加载情况。 不要假设交互式测试能证明有 Web 托管访问权限。

LocalDB 的启动和按用户所有权属于 SQL Server LocalDB 和 Windows。 PHP 扩展通过 ODBC 提供连接选项。 不要添加SQL凭证或Microsoft Entra选项来弥补实例所有权问题。