is_dir() 是不可靠的,还是这里有可以缓解的竞争条件?
Is is_dir() unreliable, or are there race conditions that can be mitigated here?
在工作中,我继承了一个具有文件上传过程的网络应用程序。此过程的一部分偶尔(每两周左右一次)触发以下错误:
PHP Warning: mkdir(): File exists in {{{file_path_name_redacted}}} on line 7
查看第 6-8 行,我们得到:
if(!is_dir($storeFolder)){
mkdir($storeFolder, 0644, TRUE);
}
鉴于此文件可能会被多个 PHP 进程命中,我相信竞争条件 可能 会在这里发挥作用。我在过去管理过的其他网站上也看到过同样的问题,类似的情况只发生过一次。
我认为发生的事情是用户双击上传按钮,这导致两个 PHP 进程几乎同时执行,如下所示:
Process 1 executes line 6 - dir does not exist
Process 2 executes line 6 - dir does not exist
Process 1 executes line 7 - directory is created
Process 2 executes line 7 - directory cannot be created as it already exists
这是竞争条件的情况吗,正如我在上面解释的那样(即是否有其他人注意到这一点),and/or是否有一些方法可以减轻错误,或者关闭警告的错误报告?
Php 检查确认 race condition exists,并建议最安全的代码编写方式是:
if (!is_dir($dir) && !mkdir($dir) && !is_dir($dir)) {
throw new \RuntimeException(sprintf('Directory "%s" could not be created', $dir));
}
感觉很奇怪,但确实有效。祝你好运。
我看到很多项目使用 , which seems to work quite well. However, it can fail when using $recursive = true
, because of a bug in PHP reported in 2005,他们以某种方式拒绝修复(是的,它 是 一个错误)。
这是到目前为止对我有用的片段:
/**
* Safer version of mkdir(..., ..., true) without race condition issues
*
* @see https://bugs.php.net/bug.php?id=35326
*
* @param string $dirname A directory path to create
* @param int $mode Permission
*/
function safeMkdirRecursive(string $dirname, int $mode = 0777): void {
$current = '';
foreach (explode(DIRECTORY_SEPARATOR, $dirname) as $part) {
$current .= $part;
if ($current !== '' && !@mkdir($current, $mode) && !is_dir($current)) {
throw new RuntimeException('Failed to create directory: ' . $current);
}
$current .= DIRECTORY_SEPARATOR;
}
}
免责声明:我没有在 Windows!
上测试过这个
在工作中,我继承了一个具有文件上传过程的网络应用程序。此过程的一部分偶尔(每两周左右一次)触发以下错误:
PHP Warning: mkdir(): File exists in {{{file_path_name_redacted}}} on line 7
查看第 6-8 行,我们得到:
if(!is_dir($storeFolder)){
mkdir($storeFolder, 0644, TRUE);
}
鉴于此文件可能会被多个 PHP 进程命中,我相信竞争条件 可能 会在这里发挥作用。我在过去管理过的其他网站上也看到过同样的问题,类似的情况只发生过一次。
我认为发生的事情是用户双击上传按钮,这导致两个 PHP 进程几乎同时执行,如下所示:
Process 1 executes line 6 - dir does not exist
Process 2 executes line 6 - dir does not exist
Process 1 executes line 7 - directory is created
Process 2 executes line 7 - directory cannot be created as it already exists
这是竞争条件的情况吗,正如我在上面解释的那样(即是否有其他人注意到这一点),and/or是否有一些方法可以减轻错误,或者关闭警告的错误报告?
Php 检查确认 race condition exists,并建议最安全的代码编写方式是:
if (!is_dir($dir) && !mkdir($dir) && !is_dir($dir)) {
throw new \RuntimeException(sprintf('Directory "%s" could not be created', $dir));
}
感觉很奇怪,但确实有效。祝你好运。
我看到很多项目使用 $recursive = true
, because of a bug in PHP reported in 2005,他们以某种方式拒绝修复(是的,它 是 一个错误)。
这是到目前为止对我有用的片段:
/**
* Safer version of mkdir(..., ..., true) without race condition issues
*
* @see https://bugs.php.net/bug.php?id=35326
*
* @param string $dirname A directory path to create
* @param int $mode Permission
*/
function safeMkdirRecursive(string $dirname, int $mode = 0777): void {
$current = '';
foreach (explode(DIRECTORY_SEPARATOR, $dirname) as $part) {
$current .= $part;
if ($current !== '' && !@mkdir($current, $mode) && !is_dir($current)) {
throw new RuntimeException('Failed to create directory: ' . $current);
}
$current .= DIRECTORY_SEPARATOR;
}
}
免责声明:我没有在 Windows!
上测试过这个