Проблемы с 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 имеет отдельный набор функций для работы с ними.
Это может быть полезно