mkdir() 函数用于创建新的目录。它可以在指定路径创建一个新的目录,并可以设置目录的权限和是否递归创建父目录。
mkdir() 函数是文件系统操作中的基础函数,常用于在文件上传、缓存文件存储、日志记录等场景中动态创建目录结构。
recursive 参数)。在创建目录之前,通常需要检查目录是否已存在,避免重复创建。
mkdir(
string $directory,
int $permissions = 0777,
bool $recursive = false,
?resource $context = null
): bool
| 参数 | 描述 |
|---|---|
| directory |
必需。要创建的目录的路径。 可以是绝对路径或相对路径。相对路径将相对于当前工作目录进行解析。 |
| permissions |
可选。目录的权限,使用八进制数表示。 默认值是 0777,表示最大可能的访问权限。实际权限会受到 umask 设置的影响。 Windows 平台上此参数被忽略。 |
| recursive |
可选。是否递归创建目录。 如果设置为 从 PHP 5.0.0 开始支持此参数。 |
| context |
可选。流上下文(stream context)资源。 用于指定流的特殊选项,例如创建 FTP 目录时需要。 |
| 返回值 | 描述 |
|---|---|
| TRUE | 目录创建成功 |
| FALSE | 目录创建失败(权限不足、路径已存在、路径无效等) |
<?php
// 创建单个目录
$dir = "/tmp/test_dir";
if (!is_dir($dir)) {
if (mkdir($dir)) {
echo "目录创建成功: $dir<br>";
} else {
echo "无法创建目录: $dir<br>";
}
} else {
echo "目录已存在: $dir<br>";
}
// 创建带特定权限的目录
$dir2 = "/tmp/test_dir2";
if (mkdir($dir2, 0755)) {
echo "目录创建成功: $dir2 (权限: 0755)<br>";
}
// 清理测试目录
if (is_dir($dir)) {
rmdir($dir);
echo "目录已删除: $dir<br>";
}
if (is_dir($dir2)) {
rmdir($dir2);
echo "目录已删除: $dir2<br>";
}
?>
<?php
// 递归创建多级目录
$deepDir = "/tmp/level1/level2/level3";
if (!is_dir($deepDir)) {
// 使用 recursive 参数
if (mkdir($deepDir, 0755, true)) {
echo "目录创建成功: $deepDir<br>";
// 验证各级目录
echo "验证各级目录:<br>";
$currentPath = "";
$parts = explode('/', trim($deepDir, '/'));
foreach ($parts as $part) {
$currentPath .= '/' . $part;
if (is_dir($currentPath)) {
echo "- $currentPath 存在<br>";
}
}
} else {
echo "无法创建目录: $deepDir<br>";
}
} else {
echo "目录已存在: $deepDir<br>";
}
// 实际应用:创建上传目录结构
function createUploadDirectory($userId, $year, $month) {
$baseDir = "/var/www/uploads";
$userDir = $baseDir . "/user_" . $userId;
$dateDir = $userDir . "/" . $year . "/" . str_pad($month, 2, '0', STR_PAD_LEFT);
if (!is_dir($dateDir)) {
if (mkdir($dateDir, 0755, true)) {
return $dateDir;
} else {
return false;
}
}
return $dateDir;
}
echo "<br>上传目录创建示例:<br>";
$uploadDir = createUploadDirectory(123, 2023, 12);
if ($uploadDir) {
echo "上传目录: $uploadDir<br>";
} else {
echo "无法创建上传目录<br>";
}
?>
<?php
// 演示权限和 umask 的影响
function createDirectoryWithPermissions($dir, $permissions = 0755) {
// 保存当前 umask
$oldUmask = umask(0);
// 创建目录
$result = mkdir($dir, $permissions, true);
// 恢复 umask
umask($oldUmask);
if ($result) {
// 获取实际权限
clearstatcache();
$actualPermissions = substr(sprintf('%o', fileperms($dir)), -4);
return [
'success' => true,
'dir' => $dir,
'requested_permissions' => sprintf('%04o', $permissions),
'actual_permissions' => $actualPermissions
];
}
return ['success' => false, 'dir' => $dir];
}
// 测试不同权限设置
echo "<h4>权限设置测试</h4>";
$testDirs = [
['/tmp/test_0755', 0755],
['/tmp/test_0777', 0777],
['/tmp/test_0700', 0700],
['/tmp/test_0750', 0750]
];
foreach ($testDirs as $test) {
list($dir, $perms) = $test;
// 清理旧目录
if (is_dir($dir)) {
rmdir($dir);
}
$result = createDirectoryWithPermissions($dir, $perms);
if ($result['success']) {
echo "目录: {$result['dir']}<br>";
echo "请求权限: {$result['requested_permissions']}<br>";
echo "实际权限: {$result['actual_permissions']}<br><br>";
// 清理
rmdir($dir);
}
}
// 实际应用:创建安全的项目目录结构
function createProjectStructure($projectName) {
$baseDir = "/var/www/projects/" . $projectName;
$dirs = [
$baseDir . "/app/controllers",
$baseDir . "/app/models",
$baseDir . "/app/views",
$baseDir . "/public/css",
$baseDir . "/public/js",
$baseDir . "/public/images",
$baseDir . "/config",
$baseDir . "/logs",
$baseDir . "/cache",
$baseDir . "/uploads"
];
$permissions = [
$baseDir => 0755,
$baseDir . "/app" => 0755,
$baseDir . "/app/controllers" => 0755,
$baseDir . "/app/models" => 0755,
$baseDir . "/app/views" => 0755,
$baseDir . "/public" => 0755,
$baseDir . "/public/css" => 0755,
$baseDir . "/public/js" => 0755,
$baseDir . "/public/images" => 0755,
$baseDir . "/config" => 0750, // 更严格的权限
$baseDir . "/logs" => 0777, // 需要写入权限
$baseDir . "/cache" => 0777, // 需要写入权限
$baseDir . "/uploads" => 0777 // 需要写入权限
];
foreach ($dirs as $dir) {
if (!is_dir($dir)) {
$perm = isset($permissions[$dir]) ? $permissions[$dir] : 0755;
if (!mkdir($dir, $perm, true)) {
throw new Exception("无法创建目录: $dir");
}
echo "创建目录: $dir (权限: " . sprintf('%04o', $perm) . ")<br>";
}
}
return $baseDir;
}
try {
echo "<br><h4>创建项目目录结构</h4>";
$projectDir = createProjectStructure("my_project");
echo "项目目录结构创建完成: $projectDir<br>";
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "<br>";
}
?>
<?php
/**
* 安全的目录创建函数
*/
class SafeDirectoryCreator {
/**
* 创建目录,带有完整的错误处理和重试机制
*/
public static function createDirectory($path, $permissions = 0755, $maxRetries = 3) {
// 检查目录是否已存在
if (is_dir($path)) {
return [
'success' => true,
'message' => "目录已存在: $path",
'path' => $path
];
}
// 验证路径
if (!self::validatePath($path)) {
return [
'success' => false,
'message' => "无效的路径: $path",
'path' => $path
];
}
// 获取父目录
$parentDir = dirname($path);
// 检查父目录是否存在且可写
if (!is_dir($parentDir)) {
$parentResult = self::createDirectory($parentDir, $permissions, $maxRetries);
if (!$parentResult['success']) {
return $parentResult;
}
} elseif (!is_writable($parentDir)) {
return [
'success' => false,
'message' => "父目录不可写: $parentDir",
'path' => $path
];
}
// 尝试创建目录(带重试)
$attempt = 0;
while ($attempt < $maxRetries) {
$attempt++;
try {
// 设置 umask 以确保正确的权限
$oldUmask = umask(0);
$result = mkdir($path, $permissions);
umask($oldUmask);
if ($result) {
clearstatcache(); // 清除缓存
// 验证目录是否真正创建
if (is_dir($path)) {
return [
'success' => true,
'message' => "目录创建成功: $path (尝试次数: $attempt)",
'path' => $path,
'attempts' => $attempt
];
}
}
// 如果失败,等待后重试
if ($attempt < $maxRetries) {
usleep(100000); // 等待100毫秒
}
} catch (Exception $e) {
// 记录错误但继续重试
error_log("创建目录时出错 (尝试 $attempt): " . $e->getMessage());
if ($attempt < $maxRetries) {
usleep(100000);
}
}
}
return [
'success' => false,
'message' => "无法创建目录: $path (最大重试次数: $maxRetries)",
'path' => $path,
'attempts' => $attempt
];
}
/**
* 验证路径是否安全有效
*/
private static function validatePath($path) {
// 检查空路径
if (empty($path)) {
return false;
}
// 检查路径长度
if (strlen($path) > 4096) {
return false;
}
// 防止目录遍历攻击
if (strpos($path, '..') !== false) {
return false;
}
// 检查非法字符(根据操作系统)
$invalidChars = ['<', '>', ':', '"', '|', '?', '*'];
foreach ($invalidChars as $char) {
if (strpos($path, $char) !== false) {
return false;
}
}
return true;
}
/**
* 批量创建目录
*/
public static function createDirectories(array $paths, $permissions = 0755) {
$results = [];
foreach ($paths as $index => $path) {
$results[$index] = self::createDirectory($path, $permissions);
}
return $results;
}
/**
* 创建临时目录
*/
public static function createTempDirectory($prefix = 'tmp_', $permissions = 0700) {
$tempDir = sys_get_temp_dir() . '/' . $prefix . uniqid();
$result = self::createDirectory($tempDir, $permissions);
if ($result['success']) {
// 设置目录在脚本结束时自动删除
register_shutdown_function(function() use ($tempDir) {
if (is_dir($tempDir)) {
self::deleteDirectory($tempDir);
}
});
}
return $result;
}
/**
* 递归删除目录
*/
public static function deleteDirectory($dir) {
if (!is_dir($dir)) {
return false;
}
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
if (is_dir($path)) {
self::deleteDirectory($path);
} else {
unlink($path);
}
}
return rmdir($dir);
}
}
// 使用示例
echo "<h4>安全目录创建示例</h4>";
// 1. 创建单个目录
$result1 = SafeDirectoryCreator::createDirectory('/tmp/safe_test/dir1');
echo "结果1: " . $result1['message'] . "<br>";
// 2. 创建已存在的目录
$result2 = SafeDirectoryCreator::createDirectory('/tmp/safe_test/dir1');
echo "结果2: " . $result2['message'] . "<br>";
// 3. 创建无效路径
$result3 = SafeDirectoryCreator::createDirectory('/tmp/../etc/passwd');
echo "结果3: " . $result3['message'] . "<br>";
// 4. 批量创建
$paths = [
'/tmp/batch_test/dir1',
'/tmp/batch_test/dir2/subdir',
'/tmp/batch_test/dir3'
];
$batchResults = SafeDirectoryCreator::createDirectories($paths);
foreach ($batchResults as $index => $result) {
echo "批量创建 {$paths[$index]}: " . $result['message'] . "<br>";
}
// 5. 创建临时目录
$tempResult = SafeDirectoryCreator::createTempDirectory('test_');
if ($tempResult['success']) {
echo "临时目录创建: " . $tempResult['path'] . "<br>";
// 临时目录会在脚本结束时自动删除
}
?>
<?php
// 创建FTP目录的示例
function createFtpDirectory($ftpServer, $username, $password, $remoteDir) {
// 创建FTP连接
$connId = ftp_connect($ftpServer);
if (!$connId) {
return "无法连接到FTP服务器: $ftpServer";
}
// 登录FTP
if (!ftp_login($connId, $username, $password)) {
ftp_close($connId);
return "FTP登录失败";
}
// 开启被动模式
ftp_pasv($connId, true);
// 创建目录
if (ftp_mkdir($connId, $remoteDir)) {
ftp_close($connId);
return "FTP目录创建成功: $remoteDir";
} else {
$error = "无法创建FTP目录: $remoteDir";
ftp_close($connId);
return $error;
}
}
// 使用流上下文创建本地目录(高级用法)
function createDirectoryWithContext($dir, $contextOptions = []) {
// 创建默认上下文选项
$defaultOptions = [
'notification' => function($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
switch($notification_code) {
case STREAM_NOTIFY_MKDIR:
echo "正在创建目录: $message<br>";
break;
case STREAM_NOTIFY_COMPLETED:
echo "操作完成<br>";
break;
}
}
];
// 合并选项
$options = array_merge($defaultOptions, $contextOptions);
// 创建流上下文
$context = stream_context_create(['notification' => $options['notification']]);
// 使用上下文创建目录
return mkdir($dir, 0755, false, $context);
}
echo "<h4>使用上下文创建目录</h4>";
// 测试带通知的目录创建
$testDir = '/tmp/context_test';
if (is_dir($testDir)) {
rmdir($testDir);
}
if (createDirectoryWithContext($testDir)) {
echo "目录创建成功(带有上下文)<br>";
}
// 实际应用:带进度通知的批量目录创建
class DirectoryCreatorWithProgress {
private $totalDirs = 0;
private $createdDirs = 0;
public function createDirectoriesWithProgress(array $dirs, $permissions = 0755) {
$this->totalDirs = count($dirs);
$this->createdDirs = 0;
$results = [];
foreach ($dirs as $index => $dir) {
$this->createdDirs++;
$progress = round(($this->createdDirs / $this->totalDirs) * 100, 2);
echo "进度: $progress% - 正在创建: $dir<br>";
if (!is_dir($dir)) {
if (mkdir($dir, $permissions, true)) {
$results[$dir] = '成功';
} else {
$results[$dir] = '失败';
}
} else {
$results[$dir] = '已存在';
}
// 模拟处理延迟(实际使用时移除)
usleep(100000);
}
echo "完成!共处理 {$this->totalDirs} 个目录<br>";
return $results;
}
}
echo "<br><h4>带进度显示的目录创建</h4>";
$creator = new DirectoryCreatorWithProgress();
$testDirs = [
'/tmp/progress/dir1',
'/tmp/progress/dir2/sub',
'/tmp/progress/dir3'
];
$results = $creator->createDirectoriesWithProgress($testDirs);
echo "<br>创建结果:<br>";
print_r($results);
?>
<?php
/**
* 跨平台的目录创建工具类
*/
class CrossPlatformDirectory {
/**
* 创建目录(自动处理平台差异)
*/
public static function create($path, $permissions = null, $recursive = true) {
// 标准化路径分隔符
$path = self::normalizePath($path);
// 设置默认权限(根据平台)
if ($permissions === null) {
$permissions = self::getDefaultPermissions();
}
// 检查目录是否已存在
if (is_dir($path)) {
return true;
}
// 获取父目录
$parentDir = dirname($path);
// 如果需要递归创建,且父目录不存在
if ($recursive && !is_dir($parentDir) && $parentDir !== $path) {
if (!self::create($parentDir, $permissions, true)) {
return false;
}
}
// 创建目录
$result = mkdir($path, $permissions, false);
if ($result) {
// 在某些平台上可能需要额外的权限设置
self::setPlatformSpecificPermissions($path, $permissions);
}
return $result;
}
/**
* 标准化路径(处理不同操作系统的路径分隔符)
*/
private static function normalizePath($path) {
// 替换Windows路径分隔符为Unix风格
$path = str_replace('\\', '/', $path);
// 处理多个连续的路径分隔符
$path = preg_replace('#/+#', '/', $path);
// 移除结尾的路径分隔符(除了根目录)
if (strlen($path) > 1 && substr($path, -1) === '/') {
$path = rtrim($path, '/');
}
return $path;
}
/**
* 获取默认权限(根据平台)
*/
private static function getDefaultPermissions() {
if (self::isWindows()) {
// Windows上权限设置被忽略,但这里返回一个合理的值
return 0755;
} else {
// Unix-like系统使用0755
return 0755;
}
}
/**
* 检查是否在Windows系统上运行
*/
private static function isWindows() {
return stripos(PHP_OS, 'WIN') === 0;
}
/**
* 设置平台特定的权限
*/
private static function setPlatformSpecificPermissions($path, $permissions) {
if (!self::isWindows()) {
// 在Unix-like系统上,确保权限正确设置
chmod($path, $permissions);
}
// Windows上不需要额外操作
}
/**
* 创建唯一的目录名
*/
public static function createUnique($basePath, $prefix = '', $maxAttempts = 100) {
$basePath = self::normalizePath($basePath);
for ($i = 0; $i < $maxAttempts; $i++) {
if ($i === 0) {
$candidate = $basePath . '/' . $prefix;
} else {
$candidate = $basePath . '/' . $prefix . '_' . uniqid();
}
if (!is_dir($candidate)) {
if (self::create($candidate)) {
return $candidate;
}
}
}
throw new Exception("无法创建唯一目录,尝试次数: $maxAttempts");
}
/**
* 创建带时间戳的目录
*/
public static function createWithTimestamp($basePath, $prefix = '') {
$timestamp = date('Y-m-d_His');
$dirName = $prefix . ($prefix ? '_' : '') . $timestamp;
$fullPath = self::normalizePath($basePath . '/' . $dirName);
if (self::create($fullPath)) {
return $fullPath;
}
return false;
}
/**
* 确保目录存在(如果不存在则创建)
*/
public static function ensureExists($path, $permissions = null) {
if (!is_dir($path)) {
return self::create($path, $permissions, true);
}
return true;
}
}
// 使用示例
echo "<h4>跨平台目录创建示例</h4>";
// 1. 基本创建
$result1 = CrossPlatformDirectory::create('/tmp/cross_test/dir1');
echo "基本创建: " . ($result1 ? '成功' : '失败') . "<br>";
// 2. 创建唯一目录
try {
$uniqueDir = CrossPlatformDirectory::createUnique('/tmp/cross_test', 'unique');
echo "唯一目录: $uniqueDir<br>";
} catch (Exception $e) {
echo "唯一目录创建失败: " . $e->getMessage() . "<br>";
}
// 3. 创建带时间戳的目录
$timestampDir = CrossPlatformDirectory::createWithTimestamp('/tmp/cross_test', 'backup');
if ($timestampDir) {
echo "时间戳目录: $timestampDir<br>";
}
// 4. 确保目录存在
$result2 = CrossPlatformDirectory::ensureExists('/tmp/cross_test/ensure_test/subdir');
echo "确保存在: " . ($result2 ? '成功' : '失败') . "<br>";
// 5. 测试Windows和Unix路径
$testPaths = [
'C:\\Windows\\Temp\\test', // Windows风格
'/var/www/html/test', // Unix风格
'relative/path/test', // 相对路径
'C:/Mixed/Style/Test', // 混合风格
];
echo "<br>路径标准化测试:<br>";
foreach ($testPaths as $path) {
$normalized = CrossPlatformDirectory::normalizePath($path);
echo "原始: $path -> 标准化: $normalized<br>";
}
// 实际应用:创建项目目录结构
function setupProjectDirectories($projectName, $basePath = null) {
if ($basePath === null) {
$basePath = CrossPlatformDirectory::isWindows() ? 'C:/projects' : '/var/www/projects';
}
$projectPath = CrossPlatformDirectory::normalizePath($basePath . '/' . $projectName);
$dirs = [
$projectPath . '/src',
$projectPath . '/tests',
$projectPath . '/docs',
$projectPath . '/config',
$projectPath . '/logs',
$projectPath . '/cache',
$projectPath . '/public/assets',
$projectPath . '/vendor'
];
// 确保所有目录都存在
foreach ($dirs as $dir) {
CrossPlatformDirectory::ensureExists($dir);
}
// 设置特定目录的权限(非Windows系统)
if (!CrossPlatformDirectory::isWindows()) {
chmod($projectPath . '/logs', 0777);
chmod($projectPath . '/cache', 0777);
}
return $projectPath;
}
echo "<br><h4>项目目录设置</h4>";
$projectPath = setupProjectDirectories('my_cross_platform_project');
echo "项目目录结构创建完成: $projectPath<br>";
?>
| 错误现象 | 可能原因 | 解决方法 |
|---|---|---|
| Warning: mkdir(): Permission denied | PHP进程没有在父目录中创建目录的权限 | 检查父目录权限,使用 is_writable() 验证 |
| Warning: mkdir(): File exists | 要创建的目录已存在 | 使用 is_dir() 检查目录是否存在 |
| Warning: mkdir(): No such file or directory | 父目录不存在且未使用递归模式 | 使用 recursive 参数或先创建父目录 |
| 目录权限与预期不符 | umask 设置影响了实际权限 | 使用 umask(0) 临时设置 umask |
| 在Windows上权限参数无效 | Windows 忽略权限参数 | 使用 chmod() 在创建后设置权限(如果支持) |
| 符号链接问题 | 路径包含符号链接 | 使用 realpath() 解析路径 |
is_dir() 检查,避免重复创建recursive = true 参数umask(0)realpath() 或自定义函数规范化路径当需要创建多个目录时,按深度排序,先创建父目录,可以减少递归调用的开销。
如果在同一脚本中多次检查同一目录,缓存检查结果可以提高性能。
如果确定目录不存在且父目录存在,可以直接创建,无需先检查。
对于大量目录创建操作,考虑使用队列或异步任务,避免阻塞主进程。