【问题标题】:Incorrect encoding of the Cyrillic's characters by BindValueBindValue 对西里尔字符的错误编码
【发布时间】:2016-10-05 01:58:32
【问题描述】:

我在 MS SQL 2008 R2 上有数据库 我安装以下软件:

  1. Ubuntu 16
    1. 阿帕奇 2
    2. PHP 7
    3. Symfony 3.0(带有leaseweb/doctrine-pdo-dblib)
  2. Windows Server 2008 R2 SP1
    1. MS SQL Server 2008 R2 SP3(带有 Cyrillic_General_CI_AS - 我无法更改排序规则)

在我的项目中,有一个控制器有以下代码

$em = $this->getDoctrine()->getManager();
$connection = $em->getConnection();
$sql="
select
    isnull(ID,'') as 'id'
    ,isnull(SNAME,'') as 'sname'
    ,isnull(FNAME,'') as 'fname'
from TBL1
where SNAMElike '%'+:sname+'%' ";

$sql =  iconv('UTF-8','Windows-1251', $sql);
$statement = $connection->prepare($sql);
$statement->bindValue(':sname', iconv('UTF-8', 'Windows-1251', $request->request->get('sname')), 'text');
$statement->execute();

在 SQL Server Profiler 中,我观察的是字符串 0xc8e2e0edeee2 而不是 Агеев。如您所见,代码来自 UTF8。

如果我不使用代码页转换,

$statement->bindValue(':sname', $request->request->get('sname'), 'text');

我在 SQL Server Profiler 中看到了字符串 0xd098d0b2d0b0d0bdd0bed0b2

如果我不使用 BindValue 并将变量放入 SQL 中,如下所示

$sname = $request->request->get('sname');
$em = $this->getDoctrine()->getManager();
$connection = $em->getConnection();
$sql="
select
    isnull(ID,'') as 'id'
    ,isnull(SNAME,'') as 'sname'
    ,isnull(FNAME,'') as 'fname'
from TBL1
where SNAMElike '%'+'".$sname."'+'%' ";

$sql =  iconv('UTF-8','Windows-1251', $sql);
$statement = $connection->prepare($sql);
$statement->execute();

我在 SQL Server Profiler 中看到了正确的字符串 Иванов


如果变量包含一个只有拉丁字符的字符串,那么没有任何问题。

我发送字符串Ivanov,然后查看字符串Ivanov


有什么问题?

【问题讨论】:

  • 您是否为连接指定了正确的编码?
  • @Andrea 你到底是什么意思? 1.config.yml中的编码 2.PHP/Symfony 文件的编码 3.POST 请求的编码
  • 数据库连接的编码设置。我不确定在使用 Doctrine 时是在哪里配置的,但是您应该可以在文档中找到。
  • @Andrea 我做到了,但对我没有帮助

标签: sql-server sql-server-2008 pdo symfony php-7


【解决方案1】:

问题出在为ext\pdo_dblib\dblib_driver.c 中的每个字符串参数dblib_handle_quoter 调用的引用函数中。它会将包含代码超出32 > char > 127 范围的字符的任何字符串转换为二进制字符串。

因此,如果Агеев 未转换为“Windows-1251”,它将变为0xd098d0b2d0b0d0bdd0bed0b2 - 一种转换为二进制字符串的UTF-8 表示(类似于Рванов)。

SQL Server 然后使用以下方法将此二进制值隐式转换为字符串:

  • 单字节转换(我猜你的情况是CP1251代码页),如果预期的数据类型是charvarchar。这里需要将参数转换为Windows-1251
  • unicode 转换,如果预期的数据类型是ncharnvarchar。这里需要将参数转换为UCS-2LE

这就是为什么(至少目前)除了使用iconv 作为参数之外别无选择的原因。

PS 如果你设置正确的连接字符集,你应该不需要转换$sql,因为它应该由 dblib 完成。

我像这样使用freetds-1.00.13PHP7 作为pdo-dblib(数据类型为varchar):

function pdo_params(...$params){
  foreach ($params as &$v){
    $v = iconv('UTF-8','Windows-1251', $v);
  }
  unset($v);
  return $params;
}

$dsn = 'dblib:dbname=DataBase;host=sql.server.host.name';

<...>

$dbh = new PDO($dsn, $user, $password,array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
$stmt = $dbh->prepare('INSERT INTO table (a,b) VALUES (?,?)');
$stmt->execute(pdo_params("Test",$msg));

【讨论】:

    猜你喜欢
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多