Dem*_*a ツ 6 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
Run Code Online (Sandbox Code Playgroud)
但奇怪的是pathinfo()的目录名部分工作正常:
pathinfo("àyà/àxà", PATHINFO_DIRNAME); // returns "àyà"
Run Code Online (Sandbox Code Playgroud)
PHP文档警告basename()和pathinfo()函数知道区域设置,但这并不能说明pathinfo(..., PATHINFO_BASENAME)和之间的不一致pathinfo(..., PATHINFO_DIRNAME),更不用说根据它们相对于拉丁语的位置,相同的非拉丁语字符将被丢弃还是被接受的事实。字符。
听起来像是一个PHP错误。
由于“基本名称”检查对于避免直接遍历对安全性而言非常重要,因此是否有任何可靠的基本名称过滤器可以很好地与Unicode输入配合使用?
我发现更改语言环境可以解决所有问题。
虽然 Apache 默认使用“C”语言环境运行,但 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
Run Code Online (Sandbox Code Playgroud)
或者更好,如果你想备份当前的语言环境并在完成后恢复它:
$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");
}
}
Run Code Online (Sandbox Code Playgroud)