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

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

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

Итак, проблема, кажется, в var_export() при работе со странными персонажами. Выполняя это в CLI, мы получаем ожидаемый результат на наших локальных машинах и на двух серверах, но не на третьем. Все они настроены на работу с 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 in 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() и "строковых функциях, которые используют текущую локаль, но работают побайтово" Документы ( просмотреть @ ответ Хакре).

Ответы [ 5 ]

6 голосов
/ 14 апреля 2012

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

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

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

возвращает

?u

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

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

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

Я предлагаю, если вы действительно хотите проанализировать проблемы, вы должны начать с hexdumps рядом с тем, как все отображается в оболочке и в других местах. Знайте, что вы можете явно определять двоичные строки b"string" (это прямая совместимость, возможно, у вас включен флаг компиляции и вы работаете с юникодом?), А также вы можете писать строки буквально, здесь шестнадцатеричный путь для 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 Документы .

0 голосов
/ 24 апреля 2012

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

0 голосов
/ 18 апреля 2012

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

0 голосов
/ 14 апреля 2012

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

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

0 голосов
/ 11 апреля 2012

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

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

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

...