is_dir() 不可靠吗?或者是否存在可以缓解的竞争条件?

问题描述 投票:0回答:2

在工作中,我继承了一个具有文件上传过程的Web应用程序。此过程的一部分偶尔(每两周左右一次)会触发以下错误:

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

这是否是竞争条件的情况,正如我上面所解释的(即是否有其他人注意到这一点),和/或是否有某种方法可以减轻错误,其他关闭警告错误报告?

php directory php-5.4
2个回答
8
投票

Php 检查确认存在竞争条件,并建议编写代码的最安全方法是:

if (!is_dir($dir) && !mkdir($dir) && !is_dir($dir)) {
    throw new \RuntimeException(sprintf('Directory "%s" could not be created', $dir));
}

更多解释(编辑:链接已失效,请参阅上面的链接)

感觉很奇怪,但确实有效。祝你好运。


2
投票

我见过很多项目都使用Hugues D 的解决方案,看起来效果相当不错。然而,使用

$recursive = true
时它可能会失败,因为 2005 年报告的 PHP 中的一个错误,他们以某种方式拒绝修复该错误(是的,它一个错误)。

这是迄今为止对我很有帮助的片段:

/**
 * 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 上测试过此功能!

© www.soinside.com 2019 - 2024. All rights reserved.