php中的UTF-8问题:var_export()返回\0个空字符,而ucfirst()、strtoupper()等行为异常

php中的UTF-8问题:var_export()返回\0个空字符,而ucfirst()、strtoupper()等行为异常,php,utf-8,localization,joyent,Php,Utf 8,Localization,Joyent,我们正在处理Joyent Solaris服务器中以前从未发生过的一个奇怪错误(在localhost或其他两个具有相同php配置的Solaris服务器中不会发生)。事实上,我不确定我们是否必须研究php或solaris,以及这是一个软件还是硬件问题 我只是想把这个贴出来,以防有人能给我们指出正确的方向 因此,在处理奇怪字符时,问题似乎出在var_export()中。 在CLI中执行此操作,我们在本地主机和其中两台服务器上获得了预期的结果,但在第三台服务器上没有。所有这些都配置为使用utf-8 $

我们正在处理Joyent Solaris服务器中以前从未发生过的一个奇怪错误(在localhost或其他两个具有相同php配置的Solaris服务器中不会发生)。事实上,我不确定我们是否必须研究php或solaris,以及这是一个软件还是硬件问题

我只是想把这个贴出来,以防有人能给我们指出正确的方向

因此,在处理奇怪字符时,问题似乎出在
var_export()
中。 在CLI中执行此操作,我们在本地主机和其中两台服务器上获得了预期的结果,但在第三台服务器上没有。所有这些都配置为使用
utf-8

$ php -r "echo var_export('ñu', true);"
在较旧的服务器和本地主机中提供此功能(预期)

'ñu'
但在服务器中,我们遇到了问题(PHP版本=>5.3.6),每当遇到“不常见”字符时,它就会添加
\0
空字符:è,á,ç。。。你说吧

'' . "\0" . '' . "\0" . 'u'
你知道我们应该去哪里吗?提前谢谢


更多信息:

  • PHP版本5.3.6
  • setlocale()
    无法解决任何问题
  • php.ini
    中的默认字符集是
    UTF-8
  • php.ini
    中的
    mbstring.internal_encoding
    设置为
    UTF-8
  • mbstring.func\u重载=0
  • 这在CLI(示例)和web应用程序(php fpm+nginx)中都会发生
  • iconv
    编码也是
    UTF-8
  • 所有文件
    utf-8
    已编码
system('locale')
返回:

LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_ALL=

到目前为止完成的一些测试(CLI):

正常行为:

$ php -r "echo bin2hex('ñu');" => 'c3b175'
$ php -r "echo mb_strtoupper('ñu');" => 'ÑU'
$ php -r "echo serialize(\"\\xC3\\xB1\");" => 's:2:"ñ";'
$ php -r "echo bin2hex(addcslashes(b\"\\xC3\\xB1\", \"'\\\\\"));" => 'c3b1'
$ php -r "echo ucfirst('iñu');" => 'Iñu'
不正常:

$ php -r "echo strtoupper('ñu');" => 'U' 
$ php -r "echo ucfirst('ñu');" => '?u' 
$ php -r "echo ucfirst(b\"\\xC3\\xB1u\");" => '?u' 
$ php -r "echo bin2hex(ucfirst('ñu'));" => '00b175'
$ php -r "echo bin2hex(var_export('ñ', 1));" => '2727202e20225c3022202e202727202e20225c3022202e202727'
$ php -r "echo bin2hex(var_export(b\"\\xC3\\xB1\", 1));" => '2727202e20225c3022202e202727202e20225c3022202e202727'

因此,问题似乎出在
var_export()
和(查看@hakre的答案)。

在php中尝试强制utf-8:

<? ini_set( 'default_charset', 'UTF-8' ); ?>


在任何页面/模板的顶部(代码的第一行)。它主要帮助我处理我的特殊角色。不确定它是否也能帮助您,请尝试一下。

可能您的所有服务器都处于良好状态。在其中一条评论中,您说您只对ucfirst()和var_export()有异议。根据这些回答,您可能会看到这一点。大多数php字符串函数在处理多字节字符串时无法正常工作。这就是php必须处理这些问题的原因


可能会有帮助

我建议您验证您遇到问题的PHP二进制文件。检查编译器标志及其使用的库

通常PHP在内部使用二进制字符串,这意味着像
ucfirst
这样的函数逐字节工作,并且只支持您的语言环境支持的内容(如果和类似配置)。看

返回

?u
这是有道理的,
ñ

LATIN SMALL LETTER N WITH TILDE (U+00F1)    UTF8: \xC3\xB1
您已经配置了一些区域设置,使PHP将
\xC3
更改为其他内容,从而打破UTF-8字节序列并使shell显示

我建议,如果你真的想分析问题,你应该从下一步开始,看看如何在shell和其他地方显示。 要知道,您可以显式定义二进制字符串
b“string”
(这是向前兼容性,可能您启用了一些编译标志,并且您使用的是unicode实验?),而且您还可以按字面意思编写字符串,这里是UTF-8的十六进制方式:

 $ php -r "echo ucfirst(b\"\\xC3\\xB1u\");"
还有更多的设置可以发挥作用,我开始在中列出一些要点


多字节
ucfirst
变体示例:

/**
 * multibyte ucfirst
 *
 * @param string $str
 * @param string|null $encoding (optional)
 * @return string
 */
function mb_ucfirst($str, $encoding = NULL)
{
    $first = mb_substr($str, 0, 1, $encoding);
    $rest = mb_substr($str, 1, strlen($str), $encoding);
    return mb_strtoupper($first, $encoding) . $rest;
}

请参见和。

我通常对所有法语字符使用
utf8_encode('ñu')
,phpunit测试,因为这项测试正在添加到-我们能否建立一个更好的单元测试套件,以便我们能够确定预期的输出应该是什么?

我首先检查每个服务器上运行的软件版本。特别是php。一个版本中的函数采用UTF-8,而另一个版本中的相同函数采用ISO-8859-1。还可以尝试比较
locale(1)
的输出和/或检查以
LC
开头的环境变量。这是否只发生在CLI上?这可能是Solaris终端处理Unicode的一些特殊情况。或者从保证不包含
NUL
字节的源代码文件运行时也会发生这种情况吗?请检查两件事,一件是在CLI上执行的php.ini(可能不同于通过Web服务器执行的php.ini),在那里将默认字符集设置为“utf-8”。其次,检查/etc/locale.gen,如果您在这台服务器上有一个en_US.UTF-8,我相信这与Solaris和PHP使用的system C库有关。我要说的是,编译后的包已经被宿主弄乱了,否则
strtoupper
一定在工作。获取正确的二进制文件。默认字符集为
php.ini
中的
UTF-8
。无论如何,谢谢。我已经做了“十六进制测试”:所有服务器,包括“坏家伙”,在执行
$php-r“echo bin2hex('ñu');”时返回
c3b175
。不确定我应该如何解释这一点…和
$php-r“echo ucfirst(b \“\\xC3\\xB1u\”;“
返回
?u
”。以及
bin2hex(ucfirst('ñu'))的含义是什么给予?(您的报告显示,对于这两种情况,PHP都在字符串中使用UTF-8序列,因此在这些系统中是相同的)。
bin2hex(ucfirst('ñu')
返回
00b175
。由于这与
strtoupper
有关,我怀疑它与编译PHP时的底层c库有关。您应该向Joynet支持人员咨询,并要求他们提供正确配置/编译的二进制文件。另外,我建议您获得一个PHP版本,它是一个更流行的PHP5.3版本,比如PHP5.3.10。谢谢Vinay,但这似乎是一个潜在的C问题,可能是编译问题。仍在努力寻找答案,但PHP似乎没有找到答案
/**
 * multibyte ucfirst
 *
 * @param string $str
 * @param string|null $encoding (optional)
 * @return string
 */
function mb_ucfirst($str, $encoding = NULL)
{
    $first = mb_substr($str, 0, 1, $encoding);
    $rest = mb_substr($str, 1, strlen($str), $encoding);
    return mb_strtoupper($first, $encoding) . $rest;
}