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

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

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

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

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

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

'ñu'

Но на сервере у нас возникают проблемы (версия PHP => 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() и "строковые функции, которые используют текущую локаль, но работают побайтово" Документы (ответ @hakre).

5 ответов

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

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

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

возвращается

?u

Это имеет смысл, ñ является

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

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

Я предлагаю, если вы действительно хотите проанализировать проблемы, вы должны начать с 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 Документы

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

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

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

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

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

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

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

Другие вопросы по тегам