带有多字节UTF-8文件名的PHP basename()和pathinfo()

时间:2017-07-23 18:25:01

标签: php unicode utf-8 pathinfo

我发现PHP函数basename()以及pathinfo()具有多字节utf-8名称的奇怪行为。 他们删除所有非拉丁字符,直到第一个拉丁字符或标点符号。但是,之后,将保留后续的非拉丁字符。

basename("àxà"); // returns "xà", I would expect "àxà" or just "x" instead
pathinfo("àyà/àxà", PATHINFO_BASENAME); // returns "xà", same as above

但奇怪的是,pathinfo()的dirname部分工作正常:

pathinfo("àyà/àxà", PATHINFO_DIRNAME); // returns "àyà"

PHP文档警告basename()pathinfo()函数可识别语言环境,但这并不能证明pathinfo(..., PATHINFO_BASENAME)pathinfo(..., PATHINFO_DIRNAME)之间的不一致,更不用说相同的事实了非拉丁字符被丢弃或被接受,具体取决于它们相对于拉丁字符的位置。

这听起来像是一个PHP错误。

从" basename"检查对于避免直接遍历的安全问题非常重要,是否有任何可靠的基本名称过滤器可以正常使用unicode输入?

1 个答案:

答案 0 :(得分:2)

我发现更改语言环境可以解决所有问题。

虽然Apache默认使用" C"默认情况下,locale,cli脚本使用utf-8语言环境运行,例如" en_US.UTF-8" (或者在我的情况下" it_IT.UTF-8")。在这些情况下,问题不会发生。

因此,Apache的解决方法包括更改" C"到" C.UTF-8"在调用这些函数之前。

setlocale(LC_ALL,'C.UTF-8');
basename("àxà"); // now returns "àxà", which is correct
pathinfo("àyà/àxà", PATHINFO_BASENAME); // now returns "àxà", which is correct

甚至更好,如果你想备份当前的语言环境并在完成后恢复它:

$lc = new LocaleManager();
$lc->doBackup();
$lc->fixLocale();
basename("àxà/àyà");
$lc->doRestore();


class LocaleManager
{
    /** @var array */
    private $backup;


    public function doBackup()
    {
        $this->backup = array();
        $localeSettings = setlocale(LC_ALL, 0);
        if (strpos($localeSettings, ";") === false)
        {
            $this->backup["LC_ALL"] = $localeSettings;
        }
        // If any of the locales differs, then setlocale() returns all the locales separated by semicolon
        // Eg: LC_CTYPE=it_IT.UTF-8;LC_NUMERIC=C;LC_TIME=C;...
        else
        {
            $locales = explode(";", $localeSettings);
            foreach ($locales as $locale)
            {
                list ($key, $value) = explode("=", $locale);
                $this->backup[$key] = $value;
            }
        }
    }


    public function doRestore()
    {
        foreach ($this->backup as $key => $value)
        {
            setlocale(constant($key), $value);
        }
    }


    public function fixLocale()
    {
        setlocale(LC_ALL, "C.UTF-8");
    }
}