【问题标题】:How to use PHP PDO ODBC with SQL Server and Unicode Characters?如何将 PHP PDO ODBC 与 SQL Server 和 Unicode 字符一起使用?
【发布时间】:2015-11-19 17:14:48
【问题描述】:

PHP PDO ODBC 似乎无法在没有手动编码的情况下存储 NVARCHAR 字符 (UTF-16)。这似乎是整个互联网上非常常见的“错误”,似乎没有人有明确的解决方案。

如何重现错误

  1. 尝试使用 PDO 插入以下日文字符:こんにちは(表示你好)
  2. 以下内容将存储在您的数据库中:ã“ã‚“ã«ã¡ã¯
  3. 然后通过 PDO 检索它并打印在屏幕上,你会得到:こんにちは

这不是那么糟糕,但也不是很好。 PHP 可以工作,但是当您有其他不在 PHP 中的应用程序从您的数据库中访问该信息时,它们会得到错误的字符串:ã“ã‚“ã«ã¡ã¯

理想情况下,您希望在任何地方都有こんにちは

症状

似乎 PDO 没有任何 NVARCHAR 概念,即使用 16 位编码的字符。事实上,您通过 PDO 传递给 SQL SERVER 或从 SQL SERVER 检索的所有内容都将是 8 位块。如何“证明”它?这里是:

  1. 你从你的日文字符串こんにちは开始。

您首先必须知道 PHP 将字符串视为二进制,并且(如果设置为这样)它将以 UTF-8 存储它们。

因此,如果我们查看こんにちは 的二进制表示,您将得到E38193E38293E381ABE381A1E381AF,这也是SQL SERVER 将为您提供ã“ã‚“ã«ã¡ã¯ 的二进制表示。 (取决于你的整理)

  1. 接下来,我们把它换成 UTF-16,因为那是 NVARCHAR 的格式。

    $utf16_string = mb_convert_encoding('こんにちは', 'UTF-16LE');

以下内容会将 PHP 中 こんにちは 的二进制表示更改为 533093306B3061306F30,这正是 SQL SERVER NVARCHAR 中 こんにちは 的二进制表示。

  1. 接下来尝试通过 PDO 将其保存在 SQL 中,您将得到以下信息:S0“0k0a0o0

S0“0k0a0o0 在 VARCHAR 中的 SQL SERVER 二进制表示是 533093306B3061306F30,这也是 こんにちは 在 NVARCHAR 中的二进制表示。

肮脏的解决方案

您可以使用以下方法通过 PDO ODBC 在 SQL SERVER 中保存和检索 unicode 数据,但这很丑...

  1. 您希望将数据转换为与 SQL SERVER NVARCHAR 完全相同的二进制表示 会存储它

    mb_convert_encoding('こんにちは', 'UTF-16LE');

  2. 您希望在 SQL SERVER 端将其作为二进制文件接收,然后将其转换为 NVARCHAR。

    @binary VARBINARY(40) SELECT @string = CONVERT(NVARCHAR(20), @binary);

  3. 此时您的数据库中有こんにちは。要检索它,您需要将其作为二进制文件重新发送到 PHP

  4. 一旦你在 PHP 中获得二进制文件,PHP 已经将其转换为十六进制字符串...所以,你想将十六进制字符串转换为二进制,然后将编码从 utf-16 更改为 utf-8

    $result = mb_convert_encoding(hex2bin($string), 'UTF-8', 'UTF-16LE');

当您将 こんにちは 回显到您的网页时,您将返回它。

基本上,这就是 SQL 驱动程序应该为我做的事情,而不是我手动做的事情。

是我忘了配置还是必须手动配置?

【问题讨论】:

    标签: php sql-server pdo odbc nvarchar


    【解决方案1】:

    不,我认为您没有忘记配置任何内容。事实上,你的解释是迄今为止我发现的关于 PHP 和 Microsoft ODBC 驱动程序之间长期存在的“问题”的最好的解释。鉴于PDO_ODBC page 说:

    在 Windows 上,PDO_ODBC ... 是连接到 Microsoft SQL Server 数据库的推荐驱动程序。

    但是,在 Windows 上,他们还提供PDO_SQLSRV,实际上看起来确实可以正常工作。

    因此,PDO_ODBC 似乎“没有任何 NVARCHAR 概念”,而不是整个 PDO。

    (如果涉及 Unicode 字符,尝试将 PHP 与 Microsoft Access ODBC 一起使用时会出现类似的问题)

    结论:PHP 对 ODBC 的支持仍然有点混乱,至少在 Microsoft 数据库方面是这样。

    【讨论】:

    【解决方案2】:

    我整天都在苦苦挣扎,试图弄清楚 ODBC/PHP 有什么可能?

    我的服务器是 Windows 2012R2 服务器,没有安装任何办公程序。仅 MS 64bit Access 驱动程序(AccessDatabaseEngine_X64.exe 文件版本 16.0.4519.1000)

    ODBC 驱动程序是 16.00.4513.1000(ACEODBC.DLL 来自 07-03-2017)

    我的服务器运行 PHP Ver 7.4 和 Apache 2.4

    我已将此添加到我的 PHP.INI:extension=php_com_dotnet.dll

    我已经尝试过 PHP/ODBC Classic 和 PHP/ODBC/PDO - 不成功

    对于测试,我总是使用丹麦岛Ærø 和波兰城市Łódź

    我在笔记本电脑 (Office 2016) 上创建 Excel 和 Access 文件并将文件复制到服务器。如前所述...我的服务器上没有安装 Office!

    我也尝试过 ADODB.Connection。我不会产生正确的输出。

    <?PHP
    $file_location = "C:\TestData\AccessPHP.accdb";
    $connStr = "Driver={Microsoft Access Driver (*.mdb, *.accdb)};charset=UTF-8;Dbq=$file_location;";
    $con = new COM("ADODB.Connection", NULL, CP_UTF8);  // specify UTF-8 code page
    $con->Open($connStr);
    $rst = new COM("ADODB.Recordset");
    $sql = "SELECT * FROM demo1";
    $rst->Open($sql, $con, 3, 3);  // adOpenStatic, adLockOptimistic
    echo 'Łódź ' . bin2hex('Łódź') . ' Length: ' . mb_strlen('Łódź') . '<br>';
    while (!$rst->EOF) {
        $p = $rst->Fields("Place");
        echo 'H: ' . bin2hex($p) . ' P: ' . $p . ' - P:(1252) ' . utf8_encode($p) . ': ' . $rst->Fields(2) . '<br>';
        $rst->MoveNext;
    }
    $rst->Close();
    $con->Close();
    

    主要灵感来自这里:Unable to retrieve UTF-8 accented characters from Access via PDO_ODBC

    此解决方案中的“问题”是使用旧的 .MDB 文件格式。 我认为应该尽量避免。让我们瞄准最新的驱动程序。 (我希望@Gord Thompson 对此发表评论。)

    所以...最大的问题是:谁有问题?微软还是 PHP?

    我转向 PowerScript。它看起来像这样:

    $query='select * from demo1' 
    $conn = New-Object System.Data.Odbc.OdbcConnection
    $conn.ConnectionString = "DSN=DemoDSN"
    $conn.open()
    $cmd = New-object System.Data.Odbc.OdbcCommand($query,$conn)
    $ds = New-Object system.Data.DataSet
    $numrows = (New-Object system.Data.odbc.odbcDataAdapter($cmd)).fill($ds) 
    $conn.close()
    foreach ($row in $ds.Tables[0].Rows ) {
        write-host "$($row[0]) `t $($row.Place)"
    }
    Write-host "Number of rows returned: $numrows"
    $ds.tables
    

    结果如预期:

    1    London
    2    Ærø
    3    Łódź
    Number of rows returned: 3
    Key Place  RowNameWithæøå
    --- -----  --------------
      1 London          1,456
      2 Ærø                 8
      3 Łódź         -12,3456
    

    好的。这表明 PHP 是有问题的。

    我安装了这个http://querytool.com的试用版 这是一款非常不错的软件,使用 MS 64 位驱动程序。

    它就像一个魅力。 Excel 和 Access 都 100% 正常

    (我个人使用的是免费版的 DBeaver,所以我会节省 180 美元)

    好的...“高级查询工具”还证明可以让 MS 驱动程序通过 ODBC 传递双字节 UTF-8 波兰字符。

    很遗憾地意识到,强大的 PHP 7.4 无法正确处理 ODBC :-(

    作为最后的奖励信息,我可以告诉您,当使用 Excel 驱动程序(顺便说一下,它是同一个 DLL)时,您需要知道,如果 WorkBook 文件包含一个名为 Sheet1 的工作表,那么您必须在查询中将其命名为 [Sheet1$]select * from [Sheet1$]

    嗯,这不是一个 100% 的解决方案,但也许它会启发某人,弄清楚如何解决这个 PHP 问题 :-)

    【讨论】:

      猜你喜欢
      • 2014-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-15
      • 2013-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多