剖析 WordPress `wp_unique_filename()` 函数的源码:如何为文件生成一个唯一的名称。

各位观众老爷们,晚上好!我是你们的老朋友,代码界的搬砖小能手。今天咱们来聊聊 WordPress 里的 wp_unique_filename() 函数,看看它是如何给文件穿上“独一无二”的马甲,避免文件重名导致的一系列尴尬事件。

开场白:文件重名引发的血案

想象一下,你上传了一张名为 cat.jpg 的可爱猫咪照片到 WordPress 网站。结果,另一位用户也上传了一张 cat.jpg。如果没有 wp_unique_filename() 这样的“文件名守护神”,那你的猫咪很可能就被别人的猫咪给顶替了,或者更糟糕的是,网站直接崩溃给你看。

所以,给文件生成一个唯一的名称,是网站稳定运行的基本保障,也是用户体验的重要一环。

wp_unique_filename() 函数:化腐朽为神奇的魔法棒

wp_unique_filename() 函数就像一根魔法棒,它能让原本普通的文件名变得独一无二。它的主要作用是:

  1. 检查文件是否存在: 首先,它会检查目标目录下是否存在同名文件。
  2. 生成新文件名: 如果存在同名文件,它会生成一个新的文件名,通常是在原文件名后面加上一个数字后缀。
  3. 循环检查: 它会不断生成新的文件名,并检查是否已存在,直到找到一个唯一的名称为止。
  4. 返回唯一文件名: 最后,它会返回这个独一无二的文件名。

源码剖析:扒开 wp_unique_filename() 的层层外衣

让我们深入 wp-includes/functions.php 文件,找到 wp_unique_filename() 函数的真面目。为了方便大家理解,我将源码拆解成几个部分,并加上详细的注释。

/**
 * 生成一个唯一的文件名。
 *
 * @since 2.0.0
 *
 * @param string $dir       上传目录。
 * @param string $filename  文件名。
 * @param string|null $ext  文件扩展名,如果为空则从文件名中提取。
 * @return string
 */
function wp_unique_filename( $dir, $filename, $ext = null ) {
    $filename = wp_basename( $filename ); // 确保文件名中没有路径信息

    if ( ! $ext ) {
        $ext = pathinfo( $filename, PATHINFO_EXTENSION ); // 获取文件扩展名
    }

    $ext = ( $ext ? '.' . $ext : '' ); // 加上点号,如果扩展名存在的话
    $filename = str_replace( $ext, '', $filename ); // 从文件名中移除扩展名

    if ( preg_match( '/.+-(d+)$/', $filename, $match ) ) {
        $filename = substr( $filename, 0, strlen( $filename ) - strlen( $match[0] ) ); // 移除旧的数字后缀
    }

    $i = 0;
    while ( file_exists( $dir . "/$filename{$i}{$ext}" ) ) {
        $i++;
        if ( $i > 100 ) { // 为了防止死循环,设置一个上限
            // 理论上不会发生,除非目录里已经有超过 100 个同名文件
            $filename = wp_basename( uniqid( $filename . '_' ) );
            $i = 0;
        }
    }

    return "$filename{$i}{$ext}"; // 返回唯一的文件名
}

代码解读:一行代码,一个故事

  1. wp_basename( $filename ):剥去路径的外衣

    $filename = wp_basename( $filename );

    wp_basename() 函数的作用是提取文件名,移除路径信息。例如,如果 $filename/path/to/my/cat.jpg,那么 $filename 会变成 cat.jpg。 这样可以确保只关注文件名本身,而忽略上传路径的影响。

  2. pathinfo( $filename, PATHINFO_EXTENSION ):揪出文件扩展名

    if ( ! $ext ) {
        $ext = pathinfo( $filename, PATHINFO_EXTENSION );
    }

    pathinfo() 函数可以获取文件的各种信息,其中 PATHINFO_EXTENSION 可以提取文件扩展名。如果函数调用时没有提供 $ext 参数,这段代码会尝试从文件名中提取扩展名。

  3. str_replace( $ext, '', $filename ):移除扩展名,准备加后缀

    $filename = str_replace( $ext, '', $filename );

    这行代码从文件名中移除扩展名,为后续添加数字后缀做准备。例如,如果 $filenamecat.jpg$ext.jpg,那么 $filename 会变成 cat

  4. preg_match( '/.+-(d+)$/', $filename, $match ):移除旧的数字后缀

    if ( preg_match( '/.+-(d+)$/', $filename, $match ) ) {
        $filename = substr( $filename, 0, strlen( $filename ) - strlen( $match[0] ) );
    }

    这段代码使用正则表达式检测文件名是否已经包含数字后缀,如果包含,则移除旧的后缀。例如,如果 $filenamecat-1.jpg,那么这段代码会将 $filename 变成 cat。 这是为了避免重复添加后缀,导致文件名变得越来越长。

  5. while ( file_exists( $dir . "/$filename{$i}{$ext}" ) ):循环检查,直到找到唯一的

    $i = 0;
    while ( file_exists( $dir . "/$filename{$i}{$ext}" ) ) {
        $i++;
        if ( $i > 100 ) {
            $filename = wp_basename( uniqid( $filename . '_' ) );
            $i = 0;
        }
    }

    这是整个函数的核心部分。它使用一个 while 循环来检查目标目录下是否存在同名文件。如果存在,则递增 $i 的值,并继续检查,直到找到一个唯一的名称为止。 为了防止出现死循环,代码中增加了一个上限 $i > 100 的判断。如果循环超过 100 次仍然找不到唯一的文件名,则使用 uniqid() 函数生成一个唯一的 ID 作为文件名,并重置 $i 的值为 0。

  6. uniqid( $filename . '_' ):终极武器,生成唯一 ID

    $filename = wp_basename( uniqid( $filename . '_' ) );

    uniqid() 函数可以生成一个基于当前时间和主机名的唯一 ID。当 $i 超过 100 时,表示目标目录下已经存在大量的同名文件,此时使用数字后缀已经很难找到唯一的名称。因此,使用 uniqid() 函数生成一个全新的文件名,可以大大提高找到唯一名称的概率。

  7. return "$filename{$i}{$ext}":大功告成,返回唯一文件名

    return "$filename{$i}{$ext}";

    循环结束后,表示已经找到了一个唯一的名称,这段代码将文件名、数字后缀和扩展名拼接在一起,并返回最终的文件名。

代码示例:实战演练

为了更好地理解 wp_unique_filename() 函数的用法,我们来看一个简单的示例。

$upload_dir = wp_upload_dir();
$dir = $upload_dir['path']; // 获取上传目录
$filename = 'cat.jpg';

$unique_filename = wp_unique_filename( $dir, $filename );

echo "原始文件名: " . $filename . "<br>";
echo "唯一文件名: " . $unique_filename;

假设上传目录 /wp-content/uploads/2023/10 中已经存在 cat.jpgcat-1.jpg 两个文件,那么这段代码的输出结果可能是:

原始文件名: cat.jpg
唯一文件名: cat-2.jpg

如果目录中存在超过 100 个 cat.jpg 及其变种,那么输出结果可能会是:

原始文件名: cat.jpg
唯一文件名: 653a23b1c6a3a.jpg

表格总结:wp_unique_filename() 函数的参数和返回值

参数 类型 描述
$dir string 上传目录的路径。
$filename string 原始文件名。
$ext string 文件扩展名,可选。如果未提供,则从 $filename 中提取。
返回值 string 生成的唯一文件名。

注意事项:避免踩坑

  1. 权限问题: 确保上传目录具有写入权限,否则 wp_unique_filename() 函数可能无法正常工作。
  2. 文件名长度: 考虑到文件系统的限制,生成的文件名不宜过长。
  3. 编码问题: 确保文件名使用 UTF-8 编码,避免出现乱码问题。
  4. 高并发场景: 在高并发场景下,可能会出现生成重复文件名的概率,需要考虑使用更可靠的唯一 ID 生成机制。

进阶技巧:定制化文件名

如果你对 wp_unique_filename() 函数默认的文件名生成规则不满意,可以考虑使用 WordPress 提供的 wp_unique_filename 过滤器来定制文件名。

add_filter( 'wp_unique_filename', 'my_custom_unique_filename', 10, 3 );

function my_custom_unique_filename( $filename, $dir, $file ) {
    // 在这里编写你的定制化文件名生成逻辑
    $new_filename = date( 'YmdHis' ) . '_' . $file; // 使用日期时间作为文件名
    return $new_filename;
}

这段代码使用 add_filter() 函数将 my_custom_unique_filename() 函数挂载到 wp_unique_filename 过滤器上。在 my_custom_unique_filename() 函数中,你可以编写自己的文件名生成逻辑,例如使用日期时间作为文件名。

总结:wp_unique_filename() 的重要性

wp_unique_filename() 函数是 WordPress 中一个非常重要的函数,它负责生成唯一的文件名,避免文件重名导致的各种问题。通过深入了解 wp_unique_filename() 函数的源码,我们可以更好地理解 WordPress 的运行机制,并根据实际需求定制文件名生成规则。

好了,今天的讲座就到这里。希望大家能够掌握 wp_unique_filename() 函数的用法,并将其应用到实际项目中。如果你还有什么疑问,欢迎随时提问。 咱们下期再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注