在PHP中创建文件夹(如果不存在):一个问题的演变
如果你曾经编写过上传文件、保存缓存或生成报告的脚本,那么你肯定遇到过这种情况:“文件夹不存在!”。这个错误会导致整个脚本崩溃,用户看到的要么是白屏,要么是(更糟糕的)一个莫名其妙的PHP错误。
Stack Overflow上关于“Create a folder if it doesn't already exist”的问题获得了数千次浏览。这并非偶然。看起来有什么复杂的呢?但魔鬼总是在细节中:权限、递归、异常处理、文件夹名称编码。在2025年,当项目变得越来越复杂,安全要求越来越严格时,正确创建目录是每个PHP开发者必须掌握的基础知识。
在本文中,我们将介绍在PHP中创建文件夹(如果不存在)的5种最佳实践。从适合新手的简单解决方案到具有竞争条件防护的生产级代码。我们开始吧!
为什么仅仅使用mkdir()不好?(一个错误的故事)
在入门阶段,很多人会这样写:
<?phpmkdir('/path/to/my/folder');?>然后他们会得到:
- Warning: mkdir(): No such file or directory — 如果父文件夹不存在。
- Warning: mkdir(): File exists — 如果文件夹已存在。
这两个警告不仅仅是“黄色文字”。在生产模式下,如果启用了严格的错误报告模式,它们可能会变成致命错误。而且这也是一个安全漏洞:攻击者可以在你的脚本之前创建一个同名文件夹(竞争条件)。
因此,现代方法总是包含检查、递归和权限控制。
方法1:使用is_dir()和mkdir()的经典方法(适合新手)
最简单易懂的方法是先检查文件夹是否存在,然后再创建它。
<?php$folder = '/var/www/uploads/images';
if (!is_dir($folder)) { mkdir($folder, 0755, true); echo "文件夹已创建!";} else { echo "文件夹已存在。";}?>这里重要的是:
is_dir()— 如果路径存在且是一个目录,则返回true。mkdir($folder, 0755, true)— 第三个参数true启用递归创建。如果父文件夹不存在,PHP会创建整个路径链(/var/www/uploads/images→ 先创建uploads,然后创建images)。0755— 权限:所有者拥有所有权限,其他人可以读取和执行。
缺点:此代码无法防止竞争条件。如果两个请求同时检查is_dir()并都尝试创建文件夹,第二个请求将触发警告。
方法2:使用@mkdir()和错误检查的生产级方法
经验丰富的开发者经常使用错误抑制操作符@,然后显式检查结果。
<?php$folder = '/var/www/uploads/images';
if (!is_dir($folder)) { // 如果文件夹已存在,则抑制警告 if (!@mkdir($folder, 0755, true) && !is_dir($folder)) { throw new \\RuntimeException('无法创建文件夹:' . $folder); }}?>为什么这更好?
- 我们抑制了警告,但没有忽略错误——如果创建失败,会抛出一个异常。
- 在
mkdir()之后进行双重检查!is_dir($folder)可以防止竞争条件:如果另一个进程在我们的检查和mkdir()调用之间创建了文件夹,我们不会崩溃。
注意:操作符@会降低性能,因为PHP仍然会生成错误,只是隐藏了它。在高负载项目中,最好使用方法3。