Проблемы UTF-8 в php: var_export() возвращает \0 нулевые символы, а ucfirst(), strtoupper() и т.д. Ведут себя странно

Мы имеем дело с странной ошибкой на сервере ****** Solaris, чего раньше не было (не происходит в локальном хосте или двух других серверах Solaris с одинаковой конфигурацией php). На самом деле, я не уверен, что мы должны смотреть на php или solaris, и если это проблема программного или аппаратного обеспечения...

Я просто хочу опубликовать это, если кто-то может указать нам в правильном направлении.

Итак, проблема, кажется, в var_export() при работе со странными символами. Выполняя это в CLI, мы получаем ожидаемый результат на наших машинах localhost и на двух серверах, но не на третьем. Все они настроены на работу с utf-8.

$ php -r "echo var_export('ñu', true);"

Дает это на более старых серверах и localhost (ожидается):

'ñu'

Но на сервере у нас возникают проблемы с (PHP Version = > 5.3.6), он добавляет \0 нулевые символы всякий раз, когда встречается с "необычным" символом: è, á, ç,... вы называете это.

'' . "\0" . '' . "\0" . 'u'

Любая идея о том, где следует смотреть? Спасибо заранее.

Дополнительная информация:

  • PHP version 5.3.6.
  • setlocale() ничего не решает.
  • default_charset utf-8 в php.ini.
  • mbstring.internal_encoding установлен в utf-8 в php.ini.
  • mbstring.func_overload = 0.
  • это происходит как в CLI (пример), так и в веб-приложении (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() и "строковых функциях, которые используют текущую локаль, но работают побайтно" Docs (см. ответ @hakre).

5 ответов

Я предлагаю вам проверить двоичный файл PHP, с которым у вас проблемы. Проверьте флаги компилятора и библиотеки, которые он использует.

Обычно PHP внутренне использует двоичные строки, что означает, что такие функции, как ucfirst, работают от байта до байта и поддерживают только вашу поддержку локали (если и как сконфигурировано). См. Подробности типа String Docs.

$ php -r "echo ucfirst('ñu');"

возвращает

?u

Это имеет смысл, ñ есть

LATIN SMALL LETTER N WITH TILDE (U+00F1) UTF8: \xC3\xB1

У вас есть какая-то локальная конфигурация, которая заставляет PHP изменять \xC3 во что-то другое, нарушая последовательность байтов UTF-8 и делая вашу оболочку отображаемой символ замены Википедия.

Я предлагаю, если вы действительно хотите проанализировать проблемы, вы должны начать с hexdumps рядом с тем, как вещи отображаются в оболочке и в других местах. Знайте, что вы можете явно определить бинарные строки b"string" (что передовая совместимость, mabye, вы включили некоторый флаг компиляции, и вы работаете в юникоде?), А также вы можете писать строки буквально, здесь hex-way для UTF- 8:

$ php -r "echo ucfirst(b\"\\xC3\\xB1u\");"

И есть намного больше настроек, которые могут сыграть свою роль, я начал перечислять некоторые пункты в ответе на Подготовка приложения PHP для использования с UTF-8.

Пример многобайтового варианта 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;
}

Смотрите mb_strtoupper Документы, а также mb_convert_case Документы.


попробуйте заставить utf-8 в php:

в самой верхней (первой строке кода) вашей любой страницы/шаблона. В основном это помогает мне с моими особыми персонажами. Не уверен, что он может помочь вам, попробуйте.


Тесты phpunit для этого добавляются в https://gist.github.com/68f5781a83a8986b9d30 - мы можем создать лучший пакет unit test, чтобы мы могли выяснить каков ожидаемый результат?


Обычно я использую utf8_encode('ñu') для всех французских символов


Возможно, все ваши серверы находятся в хорошем состоянии. В одном из комментариев вы сказали, что у вас есть только проблема с ucfirst() и var_export(). В зависимости от этих ответов вы можете посмотреть SOQ. Большая часть функции php string не будет работать должным образом при работе с многобайтовыми строками. Вот почему php имеет отдельный набор функций, чтобы справиться с ними.

Это может быть полезно

licensed under cc by-sa 3.0 with attribution.