PHP, ORM, MSSQL和Unicode,有可能让它们一起工作吗?


PHP, ORM, MSSQL and Unicode, is it possible to make these work together?

如果要将unicode数据保存到MSSQL,则需要将其保存为列类型nvarchar(等),并且必须在SQL STRING LITERALS前面加上大写n。如果使用预处理语句,则不需要在值前面加上n。

insert into tbl (col) values (N'hello')

如果你正在使用像ZF DB这样的ORM,那么你正在使用PDO连接到MSSQL(或者你正在部署在Windows上,在这种情况下,你正在使用SQLSRV或PDO_SQLSRV,一切都会工作,我的问题不再有意义)。

如果你在Linux上使用任何连接到mssql (sybase, dblib等)的PDO变体,那么你不会得到真正的准备语句,只有模拟。

如果您有模拟的预处理语句,在连接级别,SQL完全只使用字符串文字编写,这意味着您必须在任何可能的unicode值前面加上n。

  1. 是否有可能自动添加任何字符值与N与ZF?
  2. 是否有任何orm/TableGateway库允许这种类型的值操作?
  3. 是否有办法从Linux启用服务器端准备语句?
  4. 有没有人从Linux代码运行生产PHP,并让它运行对MSSQL?(如果是,你如何处理Unicode?)

编辑:我可以从Linux中获得unicode以保存到MSSQL,但是将FreeTDS协议升级到7/8似乎会破坏所有框架/库/规范,因为绝对需要改变带有N前缀的正常SQL。我不确定互联网上那些声称升级到协议版本7/8解决了他们的unicode问题的人,除了以不可移植的方式手工编写每个SQL语句之外,实际上是怎么做的。似乎使用Freetds 4.2是处理Unicode/UTF-8和mssql的最佳方式。

Update:驱动程序不再处于预览状态。MS已经为现在发布的版本提供了官方说明:https://www.microsoft.com/en-us/sql-server/developer-get-started/php-ubuntu

下面的说明现在已经过期,因为MS已经拉下了预览驱动程序下载。


有微软提供的ODBC驱动程序。这应该在这方面提供适当的行为。请参阅文章的末尾,了解我如何测试其行为(以初步的方式)。已在Azure SQL数据库V12上进行了测试。

如何在Ubuntu 16.04上安装Microsoft SQL ODBC驱动程序

这是在新的Ubuntu 16.04 Azure实例上测试的,该实例基于Canonical提供的Ubuntu 16.04 Azure映像。登录后,我使用sudo -i切换到root用户,然后:

apt-get update
apt-get -y install atool make build-essential libc6 libkrb5-3 libgss3 e2fsprogs openssl equivs
wget https://download.microsoft.com/download/2/E/5/2E58F097-805C-4AB8-9FC6-71288AB4409D/msodbcsql-13.0.0.0.tar.gz
atool -x msodbcsql-13.0.0.0.tar.gz
rm msodbcsql-13.0.0.0.tar.gz
pushd msodbcsql-13.0.0.0/
./build_dm.sh --accept-warning | tee build_dm_result.txt
command=$(cat build_dm_result.txt | grep "Run the command" | cut -d"'" -f2)
rm build_dm_result.txt
sh -c "$command"
popd
echo "/usr/lib64" > /etc/ld.so.conf.d/microsoft-lib64.conf
ldconfig
pushd msodbcsql-13.0.0.0/
./install.sh  install --accept-license

用您自己的证书替换下面命令中的服务器和证书。

sqlcmd -S somedatabase.database.windows.net -U someuser -P somepassword
此时,您应该能够发出SQL命令。好的,让它在php中工作。

与php一起使用

我们必须确保libodbc1包没有被安装,并且它不会被安装,因为它将被php使用而不是我们自定义的编译包,这将导致编码问题。

cat > libodbc1<<EOL
Section: misc
Priority: optional
Standards-Version: 3.9.2
Package: libodbc1
Version: 9999
Description: fake pkg, so that we satisfy the dependency of php7-odbc, so that we can keep our custom built libodbc
EOL
equivs-build libodbc1
dpkg -i libodbc1_9999_all.deb
rm libodbc1
rm libodbc1_9999_all.deb
apt-get install php7.0-odbc php7.0-cli

此时,您应该将其作为ODBC驱动程序可用。

测试其行为

创建一个php文件test.php,使用UTF-8编码,包含以下内容。将连接字符串中的服务器、数据库和凭据替换为您自己的。

<?php
$pdo = new PDO('odbc:Driver={ODBC Driver 13 for SQL Server};Server=tcp:somedatabase.database.windows.net,1433;Database=somedatabase;Uid=someuser@somedatabase;Pwd=somepassword;Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;');
$str = 'Árvíztűrő tükörfúrógép, and... 你好,世界';
$pdo->prepare("DROP TABLE test")->execute();
$pdo->prepare("CREATE TABLE test(a NVARCHAR(MAX))")->execute();
$stmt = $pdo->prepare("INSERT INTO test VALUES(?)");
$stmt->bindParam(1, $str);
$stmt->execute();
$stmt = $pdo->prepare("SELECT * FROM test");
$stmt->execute();
$data = $stmt->fetchall();
var_dump($data[0][0]==$str); //Returns true
$stmt = $pdo->prepare("SELECT * FROM test WHERE a=?");
$stmt->bindParam(1, $str);
$stmt->execute();
$data = $stmt->fetchall();
var_dump($data[0][0]==$str); //Returns true

php -f test.php运行这个显示我们得到字符串没有任何损坏。此外,字符串从SQL Server管理工作室看起来也不错。我在Azure Portal的Performance Insight页面上观察到以下查询:(@P1 nvarchar(max))INSERT INTO test VALUES(@P1),所以显然使用了准备好的语句,所以我认为它可以处理你(和我)的场景。

(这篇文章是很大的帮助,同时试图得到这个工作:http://www.codesynthesis.com/~boris/blog/2011/12/02/microsoft-sql-server-odbc-driver-linux/谢谢鲍里斯!)