«Create a folder if it doesn't already exist»:如何在2025年避免重复造轮子
\n\n您是否曾经编写过需要保存文件的脚本,却突然遇到错误 Warning: mkdir(): No such file or directory?如果是,您并不孤单。关于 «Create a folder if it doesn't already exist» 的问题在 Stack Overflow 上获得了数千次浏览,这并非没有原因。看起来很简单,对吧?但在实践中,新手(甚至老手)常常会陷入陷阱:权限问题、递归创建、竞态条件。
在本文中,我们将分析 2025年PHP创建文件夹的最佳实践。您将学习如何正确使用 mkdir(),避免常见错误,并编写在关键时刻不会崩溃的可靠代码。开始吧!
为什么«Create a folder if it doesn't exist»问题如此受欢迎?
\n\n几乎每两个Web项目中就有一个会以某种方式处理文件:上传头像、生成报告、缓存、日志。在任何情况下,都需要确保目标目录存在。如果目录不存在,脚本会报错,用户会看到“500 Internal Server Error”,而您则需要在凌晨三点排查错误。
\n\n问题在于,PHP函数 mkdir() 不会自动创建父目录。许多开发者会写出类似这样的代码:
if (!file_exists($path)) {\n mkdir($path);\n}\n\n\n这确实有效……直到发生 race condition(竞态条件)。如果两个脚本同时检查目录是否存在,并都尝试创建它——其中一个会因错误而崩溃。在2025年,随着网站负载的增加,这类错误变得至关重要。
\n\nPHP中的 mkdir() 函数:全面解析
\n\n让我们回顾一下函数签名:
\n\nmkdir(string $directory, int $permissions = 0777, bool $recursive = false, resource $context = ?): bool\n\n\n- \n
- $directory — 要创建的目录路径。 \n
- $permissions — 权限(默认0777,但受umask影响)。 \n
- $recursive — 如果为
true,则递归创建所有缺失的父目录。 \n - $context — 流上下文(很少使用)。 \n
关键秘密在于 $recursive 参数。大多数问题都可以通过它解决。但让我们一步步来。
简单方法:is_dir() + mkdir()
\n\n检查目录是否存在的最明显且正确的方法是使用 is_dir(),而不是 file_exists()。为什么?因为 file_exists() 对于同名文件也会返回 true,从而导致错误。
$dir = '/path/to/upload';\n\nif (!is_dir($dir)) {\n mkdir($dir, 0755, true);\n}\n\n\n这里我们:
\n- \n
- 检查目录不存在且不是文件。 \n
- 以权限0755创建目录(所有者拥有全部权限,其他人可读可执行)。 \n
- 使用
true进行递归创建——如果父目录/path/to不存在,它也会被创建。 \n
这种方法覆盖了90%的情况。但如果两个请求同时执行这段代码呢?
\n\n防止竞态条件(race condition)
\n\n在高负载系统中,两个脚本可能同时尝试创建目录。为了避免错误,可以使用 @(抑制错误),或者更好的方法是检查 mkdir() 的结果:
$dir = '/path/to/cache';\n\nif (!is_dir($dir)) {\n // 尝试创建。如果另一个进程已经创建了目录,也没关系\n if (!mkdir($dir, 0755, true) && !is_dir($dir)) {\n throw new \\RuntimeException('无法创建目录:' . $dir);\n