PHP rename() 函数

定义和用法

rename() 函数用于重命名文件或目录,也可以移动文件到新位置。这个函数既可以改变文件名,也可以改变文件的位置,或者同时改变两者。

当目标文件已存在时,如果权限允许,rename() 会覆盖目标文件。在Windows系统中,如果目标文件已存在且没有被占用,操作会成功;在Unix/Linux系统中,如果目标文件已存在,它会被替换。

语法

rename ( string $oldname , string $newname [, resource $context ] ) : bool

参数说明:

  • $oldname:原始文件或目录的路径(必需)
  • $newname:新的文件或目录路径(必需)
  • $context:上下文资源(可选,通常用于流操作)

参数详解

参数 描述
oldname

原始文件或目录的路径。必需参数。

  • 可以是相对路径或绝对路径
  • 必须指向已存在的文件或目录
  • PHP必须有相应的读写权限
newname

新的文件或目录路径。必需参数。

  • 如果路径中包含目录,该目录必须存在
  • 如果目标文件已存在且可写,将被覆盖
  • 可以跨文件系统移动文件(某些系统限制)
context

上下文资源。可选参数。

  • 用于指定流的上下文参数
  • 通常通过 stream_context_create() 创建
  • 在大多数情况下不需要使用

返回值

  • 成功时:返回 true
  • 失败时:返回 false,通常是由于以下原因:
    • 原始文件不存在
    • 权限不足
    • 目标目录不存在
    • 文件正在被使用(Windows系统)
    • 跨设备移动文件时遇到限制

示例

示例 1:基本重命名

重命名一个文件:

<?php
$oldname = 'old_file.txt';
$newname = 'new_file.txt';

if (rename($oldname, $newname)) {
    echo "文件重命名成功!";
} else {
    echo "文件重命名失败。";
}
?>
示例 2:移动文件到新目录

将文件移动到不同的目录:

<?php
$oldname = 'uploads/temp.jpg';
$newname = 'images/profile.jpg';

// 确保目标目录存在
if (!file_exists('images')) {
    mkdir('images', 0755, true);
}

if (rename($oldname, $newname)) {
    echo "文件移动成功!";
} else {
    echo "文件移动失败。";

    // 获取更多错误信息
    $error = error_get_last();
    if ($error) {
        echo " 错误信息: " . $error['message'];
    }
}
?>
示例 3:重命名目录

重命名一个目录:

<?php
$olddir = 'temp_data';
$newdir = 'archived_data';

if (is_dir($olddir)) {
    if (rename($olddir, $newdir)) {
        echo "目录重命名成功!";
    } else {
        echo "目录重命名失败。";
    }
} else {
    echo "源目录不存在。";
}
?>
示例 4:批量重命名文件

批量重命名一个目录中的所有文件:

<?php
$directory = 'photos';
$prefix = 'vacation_';
$counter = 1;

if (is_dir($directory)) {
    $files = scandir($directory);

    foreach ($files as $file) {
        if ($file === '.' || $file === '..') continue;

        $oldpath = $directory . '/' . $file;
        $extension = pathinfo($file, PATHINFO_EXTENSION);
        $newpath = $directory . '/' . $prefix . $counter . '.' . $extension;

        if (rename($oldpath, $newpath)) {
            echo "重命名 {$file} 为 {$prefix}{$counter}.{$extension}<br>";
            $counter++;
        } else {
            echo "重命名 {$file} 失败<br>";
        }
    }
}
?>
示例 5:使用上下文

使用上下文资源进行重命名操作:

<?php
// 创建上下文(虽然rename()通常不需要特殊的上下文)
$context = stream_context_create([
    'http' => [
        'method' => 'GET',
        'header' => "User-Agent: PHP\r\n"
    ]
]);

$oldname = 'data.txt';
$newname = 'backup_data.txt';

if (rename($oldname, $newname, $context)) {
    echo "使用上下文重命名成功!";
} else {
    echo "重命名失败。";
}
?>

注意事项和最佳实践

重要提示
  • 权限检查: 确保PHP对源文件有读取权限,对目标位置有写入权限
  • 目标目录存在: 目标路径中的目录必须已存在,rename() 不会自动创建目录
  • 文件锁定: 在Windows系统中,如果文件正在被其他进程使用,重命名会失败
  • 跨设备移动: 在某些系统上,rename() 不能跨不同的文件系统/设备移动文件
  • 符号链接: 如果源文件是符号链接,rename() 会重命名链接本身,而不是链接指向的文件
  • 错误处理: 总是检查返回值,并使用 error_get_last() 获取详细错误信息
安全注意事项
  • 验证输入: 如果文件名来自用户输入,务必验证和清理路径
  • 路径遍历: 防止路径遍历攻击,使用 basename()realpath() 处理路径
  • 权限最小化: 为文件和目录设置最小必要权限
  • 备份重要文件: 在重命名或移动重要文件前创建备份
跨平台兼容性
<?php
function safe_rename($oldname, $newname) {
    // 验证文件存在
    if (!file_exists($oldname)) {
        return ['success' => false, 'error' => '源文件不存在'];
    }

    // 确保目标目录存在
    $target_dir = dirname($newname);
    if (!is_dir($target_dir)) {
        if (!mkdir($target_dir, 0755, true)) {
            return ['success' => false, 'error' => '无法创建目标目录'];
        }
    }

    // 尝试重命名
    if (rename($oldname, $newname)) {
        return ['success' => true, 'error' => null];
    } else {
        $error = error_get_last();
        return ['success' => false, 'error' => $error['message'] ?? '未知错误'];
    }
}

// 使用安全的重命名函数
$result = safe_rename('source.txt', 'backup/source.txt');
if ($result['success']) {
    echo "操作成功!";
} else {
    echo "操作失败: " . $result['error'];
}
?>

常见问题解答

rename() 通常是一个原子操作(在同一个文件系统内),而 copy()+unlink() 不是原子的。rename() 通常更快,因为它只修改目录条目而不复制文件数据。但如果跨不同的文件系统,rename() 实际上会执行复制+删除操作。

如果目标在不同的文件系统上,rename() 可能失败。这种情况下应该使用 copy() 复制文件,然后用 unlink() 删除原文件。一些操作系统允许跨文件系统的 rename(),但具体行为因系统而异。

在同一个文件系统内重命名文件会保留所有文件属性,包括权限、所有者和时间戳。如果文件被移动到不同的文件系统(此时实际是复制+删除),新文件会继承目标目录的默认权限。

不可以。rename() 只能用于本地文件系统。对于远程文件(通过HTTP、FTP等),需要特定的协议支持,通常使用相应的库函数。