PHP7 UTF-8 имена файлов на сервере Windows, новое явление, вызванное ZipArchive

Обновить:

Готовя отчет об ошибках для великих людей, делающих возможным использование PHP 7, я еще раз пересмотрел свое исследование и попытался объединить его в несколько простых строк кода. При этом я обнаружил, что сам PHP не является причиной проблемы. Я поделюсь своими результатами здесь, когда я закончу. Просто чтобы вы знали и не теряли время или что-то в этом роде 🙂


Сводка: PHP7 теперь может писать имена файлов в формате UTF-8, но не может получить к ним доступ?

Преамбула: я прочитал около 10-15 статей, касающихся этой темы, но они не помогли мне решить проблему, и все они старше, чем версия PHP7. Мне кажется, что это, вероятно, новая проблема, и мне интересно, может ли это быть ошибкой. Я провел много времени, экспериментируя с en / decoding для строк и пытаясь найти способ заставить его работать — безрезультатно.

Всем доброго времени суток и привет из Германии (вставьте здесь робкое замечание «не мой родной язык»), я надеюсь, вы поможете мне с этим новым явлением, с которым я столкнулся. Кажется, это «новый» в том смысле, что он пришел с PHP 7.

Я думаю, что большинство людей, работающих с PHP в системе Windows, очень хорошо знакомы с проблемой имен файлов и прозрачной оболочки PHP, которая управляет доступом к файлам с именами, отличными от ASCII (или windows-1252 или любой другой системной кодовой страницей).

Я не совсем уверен, как подходить к предмету, и, как вы можете видеть, я не очень разбираюсь в сочинении вопросов, поэтому, пожалуйста, не отрывайте мне голову. И да, я буду стремиться к тому, чтобы это было коротким. Вот так:

Первый симптом: после обновления до PHP7 у меня иногда возникали проблемы с доступом к файлам, сгенерированным моим программным обеспечением. Иногда это работало как обычно, иногда нет. Я обнаружил, что разница в том, что PHP7 теперь может писать имена файлов в формате UTF-8, но не может получить доступ к файлам с этими именами.

После генерации указанных файлов в двух отдельных «идентичных» системах (различающихся только в версии PHP), вот как имена файлов на жестком диске:

PHP 5.5: Lokaltest_KG_æ¼ ¢ å — _æ ± ‰ å — _Krümhold-DEZ1604-140081-complete.zip

PHP 7: Lokaltest_KG_ 漢字 _ 汉字 _Krümhold-DEZ1604-140081-complete.zip

Великолепно, PHP 7 способен записывать unicode-имена файлов на HDD, а UTF-16 используется в windows afaik. Недостатком является то, что когда я пытаюсь получить доступ к этим файлам, например, с is_file() PHP 5.5 работает, но PHP 7 нет.

Рассмотрим этот фрагмент кода (примечание: я «взломал» эту функцию, потому что это был самый простой способ, он не был написан для этой цели). Эта функция вызывается после того, как генерируется zip-файл, принимающий имя клиента и другие значения для определения правильного имени. Те выходят из базы данных. База данных и внутренняя кодировка PHP — UTF-8. clearstatcache само по себе это не нужно, но я включил его, чтобы прояснить ситуацию. Важный: Все, что происходит, делается с помощью PHP7, никакой другой объект не отвечает за создание zip-файла. Чтобы быть точным это сделано с class ZipArchive, На самом деле даже не имеет значения, что это zip-архив, дело в том, что имя файла и содержимое файла созданы PHP7 — успешно.

public static function downloadFileAsStream( $file )
{
clearstatcache();
print $file . "<br/>";
var_dump(is_file($file));
die();
}

Выход:

D:/htdocs/otm/.data/_tmp/Lokaltest_KG_漢字_汉字_Krümhold-DEZ1604-140081-complete.zip
bool(false)

Таким образом, PHP7 может сгенерировать файл — они действительно существуют на жестком диске и являются законными и доступными и все — но не в состоянии получить к ним доступ. is_file не единственная функция, которая не работает, file_exists() делает тоже к примеру.

Небольшой эксперимент с преобразованием кодировки, чтобы дать вам представление о том, что я пробовал:

public static function downloadFileAsStream( $file )
{
clearstatcache();
print $file . "<br/>";
print mb_detect_encoding($file, 'ASCII,UTF-16,windows-1252,UTF-8', false) . "<br/>";
print mb_detect_encoding($file, 'ASCII,UTF-16,windows-1252,UTF-8', true) . "<br/>";

if (($detectedEncoding = mb_detect_encoding($file, 'ASCII,UTF-16,windows-1252,UTF-8', true)) != 'windows-1252')
{
$file = mb_convert_encoding($file, 'UTF-16', $detectedEncoding);
}

print $file . "<br/>";
var_dump(is_file($file));
die();
}

Выход:

D:/htdocs/otm/.data/_tmp/Lokaltest_KG_漢字_汉字_Krümhold-DEZ1604-140081-complete.zip
UTF-8
UTF-8
D:/htdocs/otm/.data/_tmp/Lokaltest_KG_o"[W_lI[W_Kr�mhold-DEZ1604-140081-complete.zip
NULL

Таким образом, преобразование из UTF-8 (база данных / внутренняя кодировка) в UTF-16 (файловая система Windows) также не работает.

Я нахожусь в конце своей веревки здесь, и, к сожалению, проблема очень важна для нас, так как мы не можем обновить наши системы с этой проблемой, вырисовывающейся на заднем плане. Я надеюсь, что кто-то может пролить немного света на это. Извините за длинный пост, я не уверен, насколько хорошо я мог донести свою мысль.


Дополнение:

$file = utf8_decode($file);
var_dump(is_file($file));
die();

Поставляет false для имени файла с японскими буквами. Когда я изменяю ввод, использованный для создания имени файла, таким образом, чтобы имя файла теперь было Lokaltest_KG_Krümhold-DEZ1604-140081-complete.zip, приведенный выше код выдает значение true. Так utf8_decode помогает, но только с небольшой частью юникода, немецкие умлауты?

6

Решение

Отвечая на мой собственный вопрос здесь: На самом деле плохим парнем был компонент ZipArchive, который создавал файлы с неправильно закодированными именами файлов. Я написал полезный отчет об ошибке: https://bugs.php.net/bug.php?id=72200

Рассмотрим этот короткий сценарий:

print "php default_charset: ".ini_get('default_charset')."\n"; // just 4 info (UTF-8)

$filename = "bugtest_müller-lüdenscheid.zip"; // just an example
$filename = utf8_encode($filename); // simulating my database delivering utf8-string

$zip = new ZipArchive();
if( $zip->open($filename, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true )
{
$zip->addFile('bugtest.php', 'bugtest.php'); // copy of script file itself
$zip->close();
}

var_dump( is_file($filename) );  // delivers ?

выход:

output PHP 5.5.35:
php default_charset: UTF-8
bool(true)

output PHP 7.0.6:
php default_charset: UTF-8
bool(false)
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]