Windows服务器上的PHP7 UTF-8文件名,由ZipArchive引起的新现象

tho*_*lin 6 php windows filenames utf-8 php-7

更新:

准备一个错误报告给那些使PHP 7成为可能的伟大人物我再次修改了我的研究,并试图将其简化为几行简单的代码.在这样做时,我发现PHP本身不是问题的原因.我完成后,我会在这里分享我的结果.只是你知道,不要浪费你的时间或东西:)


概要:PHP7现在似乎能够编写UTF-8文件名但无法访问它们?

序言:我在这里阅读了大约10-15篇文章,但是它们并没有帮助我解决问题,它们都比PHP7版本更早.在我看来,这可能是一个新问题,我想知道它是否可能是一个错误.我花了很多时间尝试对字符串进行解码和解码,并试图找到一种方法使其工作 - 无济于事.

大家好日子和来自德国的问候(在这里插入害羞的非母语 - 评论),我希望你能帮助我解决我遇到的这种新现象.从PHP 7开始,它似乎是"新的".

我认为大多数在Windows系统上使用PHP的人都非常熟悉文件名的问题以及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

Splendid,PHP 7能够在HDD上编写unicode文件名,而UTF-16则在Windows上使用.现在的缺点是,当我尝试访问这些文件时,例如is_file()PHP 5.5的工作,但PHP 7没有.

考虑一下这段代码片段(注意:我"入侵"了这个函数,因为它是最简单的方法,它不是为此而编写的).生成zip文件后调用此函数,获取客户名称和其他值以确定正确的名称.那些来自数据库.PHP的数据库和内部编码都是UTF-8.clearstatcache本身并不是必要的,但我把它包括在内以使事情更清楚.重要提示:所有发生的事情都是使用PHP7完成的,没有其他实体负责创建zip文件.确切地说,它完成了class ZipArchive.实际上它甚至不是一个zip-archive,重点是文件的文件名和内容是由PHP7创建的 - 成功.

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

输出是:

D:/htdocs/otm/.data/_tmp/Lokaltest_KG_??_??_Krümhold-DEZ1604-140081-complete.zip
bool(false) 
Run Code Online (Sandbox Code Playgroud)

所以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();
}       
Run Code Online (Sandbox Code Playgroud)

输出是:

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 
Run Code Online (Sandbox Code Playgroud)

因此,从UTF-8(数据库/内部编码)转换为UTF-16(Windows文件系统)似乎也不起作用.

我在这里结束了,遗憾的是这个问题对我们来说非常重要,因为我们无法在后台隐藏这个问题来更新我们的系统.我希望有人可以对此有所了解.对不起,很长的帖子,我不知道我能说得多好.


加成:

$file = utf8_decode($file);
var_dump(is_file($file));
die();
Run Code Online (Sandbox Code Playgroud)

使用日文字母为文件名提供false.当我更改用于创建文件名的输入时,现在文件名为Lokaltest_KG_Krümhold-DEZ1604-140081-complete.zip,上面的代码为true.所以有utf8_decode帮助,但只有一小部分unicode,德国变形金刚?

tho*_*lin 1

在这里回答我自己的问题:真正的坏孩子是 ZipArchive 组件,它创建了文件名编码不正确的文件。我写了一份希望有用的错误报告:https://bugs.php.net/bug.php ?id=72200

\n\n

考虑这个简短的脚本:

\n\n
print "php default_charset: ".ini_get(\'default_charset\')."\\n"; // just 4 info (UTF-8)\n\n$filename = "bugtest_m\xc3\xbcller-l\xc3\xbcdenscheid.zip"; // just an example\n$filename = utf8_encode($filename); // simulating my database delivering utf8-string\n\n$zip = new ZipArchive();\nif( $zip->open($filename, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true )\n{\n    $zip->addFile(\'bugtest.php\', \'bugtest.php\'); // copy of script file itself\n    $zip->close();\n}\n\nvar_dump( is_file($filename) );  // delivers ?\n
Run Code Online (Sandbox Code Playgroud)\n\n

输出:

\n\n
output PHP 5.5.35:\n    php default_charset: UTF-8\n    bool(true)\n\noutput PHP 7.0.6:\n    php default_charset: UTF-8\n    bool(false)\n
Run Code Online (Sandbox Code Playgroud)\n