带有多字节 UTF-8 文件名的 PHP basename() 和 pathinfo()
Posted
技术标签:
【中文标题】带有多字节 UTF-8 文件名的 PHP basename() 和 pathinfo()【英文标题】:PHP basename() and pathinfo() with Multibytes UTF-8 file names 【发布时间】:2017-12-29 07:49:13 【问题描述】:我发现 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() 的目录名部分工作正常:
pathinfo("àyà/àxà", PATHINFO_DIRNAME); // returns "àyà"
PHP 文档警告说 basename() 和 pathinfo() 函数可以识别语言环境,但这并不能证明 pathinfo(..., PATHINFO_BASENAME)
和 pathinfo(..., PATHINFO_DIRNAME)
之间的不一致是合理的,更不用说相同的非拉丁字符被丢弃或接受,具体取决于它们相对于拉丁字符的位置。
这听起来像是一个 PHP 错误。
由于“basename”检查对于避免directoy 遍历的安全问题非常重要,是否有任何可靠的basename 过滤器可以很好地处理unicode 输入?
【问题讨论】:
【参考方案1】:我发现更改语言环境可以解决所有问题。
虽然 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
或者甚至更好,如果您想备份当前语言环境并在完成后恢复它:
$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");
【讨论】:
对我不起作用。改用这个: setlocale(LC_ALL,'en_US.UTF-8'); 请记住,在您更改语言环境的那一瞬间,整个进程的语言环境将不同,并且在同一进程中运行的任何其他脚本将报告不同的语言环境。 (由于 99,(9)% 的 Web 服务器运行非线程安全的 PHP。不仅 Apache mod_php,FPM 也受到影响。) 完美,你值得拥有很多+以上是关于带有多字节 UTF-8 文件名的 PHP basename() 和 pathinfo()的主要内容,如果未能解决你的问题,请参考以下文章