详解 WordPress `get_attachment_id_from_url()` 函数源码:通过 URL 反向查询附件 ID。

各位观众,晚上好!我是今晚的讲师,咱们今天来聊聊 WordPress 里面一个挺实用的小函数—— get_attachment_id_from_url()。 听名字就知道,这货干的就是通过附件的 URL 来查到它在数据库里的 ID。 就像警察叔叔通过车牌号查到车主是谁一样,都是反向查找,很酷炫对不对?

开场白:为什么要反向查找?

你可能会问,为啥我们需要这么个函数?直接拿 ID 用不香吗? 想象一下,你正在解析一段用户提交的文本,里面可能包含各种各样的图片链接。这些链接可能来自你的 WordPress 站点,也可能来自别的网站。如果你想替换掉那些属于你站点的图片链接,或者你想统计一下文章里用了多少张图片,你就需要知道这些链接是不是对应着你 WordPress 媒体库里的附件。

所以,get_attachment_id_from_url() 就派上用场了。它可以帮你判断一个 URL 是否属于你的站点,并且如果属于,还能告诉你这个附件的 ID 是多少。

源码剖析:一层层剥开它的心

好了,废话不多说,咱们直接看源码(基于 WordPress 最新版,可能会有细微差异,但原理不变):

function get_attachment_id_from_url( $url ) {
    global $wpdb;

    $attachment_id = false;

    // If there is no URL, return.
    if ( '' === $url ) {
        return false;
    }

    // Get the upload directory paths.
    $upload_dir_paths = wp_upload_dir();

    // Make sure the upload path base directory exists in the attachment URL, to verify that we're working with a media library image.
    if ( false !== strpos( $url, $upload_dir_paths['baseurl'] ) ) {

        // If this is the URL of an auto-generated thumbnail, get the URL of the original image.
        $url = preg_replace( '/-d+xd+(?=.(jpg|jpeg|png|gif|bmp))$/i', '', $url );

        // Remove the upload path base directory from the attachment URL.
        $attachment_url = str_replace( $upload_dir_paths['baseurl'] . '/', '', $url );

        // Finally, run a custom database query to get the attachment ID from the database.
        // Have to use the {$wpdb->posts} AND {$wpdb->postmeta} tables to find the attachment ID.
        $attachment_id = $wpdb->get_var( $wpdb->prepare(
            "SELECT wposts.ID
            FROM {$wpdb->posts} wposts, {$wpdb->postmeta} wpostmeta
            WHERE wpostmeta.post_id = wposts.ID
            AND wpostmeta.meta_key = '_wp_attached_file'
            AND wpostmeta.meta_value = %s
            AND wposts.post_type = 'attachment'",
            $attachment_url
        ) );

    }

    return $attachment_id;
}

看起来代码不少,但别怕,咱们一步步拆解:

  1. 参数校验:

    if ( '' === $url ) {
    return false;
    }

    这个很简单,如果传进来的 URL 是空的,那就直接返回 false,没啥好查的。

  2. 获取上传目录信息:

    $upload_dir_paths = wp_upload_dir();

    wp_upload_dir() 函数会返回一个数组,里面包含了你的 WordPress 站点上传目录的相关信息,比如:

    键名 含义 示例
    path 上传目录的物理路径 /var/www/html/wp-content/uploads
    url 上传目录的 URL http://example.com/wp-content/uploads
    subdir 当前年月子目录 /2023/10
    basedir 上传目录的物理路径 (和 path 一样) /var/www/html/wp-content/uploads
    baseurl 上传目录的 URL (和 url 一样) http://example.com/wp-content/uploads
    error 是否有错误,通常是布尔值 false 或错误消息字符串。 false

    我们主要用到的是 baseurl,也就是上传目录的 URL,用来判断传进来的 URL 是否属于我们站点的媒体库。

  3. 判断 URL 是否属于本站:

    if ( false !== strpos( $url, $upload_dir_paths['baseurl'] ) ) {
    // ...
    }

    strpos() 函数用来查找一个字符串在另一个字符串中首次出现的位置。如果 $url 中包含 $upload_dir_paths['baseurl'],说明这个 URL 很可能就是我们站点的附件 URL。

  4. 处理缩略图 URL:

    $url = preg_replace( '/-d+xd+(?=.(jpg|jpeg|png|gif|bmp))$/i', '', $url );

    WordPress 会自动生成一些缩略图,这些缩略图的 URL 后面会加上 -尺寸 这样的后缀,比如 image-300x200.jpg。 这个正则表达式的作用就是把这个后缀去掉,还原成原始图片的 URL。 preg_replace 是一个强大的函数,可以用来进行各种复杂的字符串替换。 这个正则表达式稍微解释一下:

    • -d+xd+: 匹配一个连字符 -,然后跟着一串数字 d+,一个 x,再跟着一串数字 d+。 这就是缩略图尺寸的表示方式。
    • (?=.(jpg|jpeg|png|gif|bmp)): 这是一个正向肯定预查(Positive Lookahead),它表示匹配的内容后面必须跟着 .jpg, .jpeg, .png, .gif, 或者 .bmp 这些文件扩展名。 但是预查的内容本身不会被匹配到。
    • $/i: $ 表示匹配字符串的结尾,i 表示忽略大小写。

    总的来说,这个正则表达式的作用就是把 URL 中类似于 -300x200.jpg 这样的缩略图后缀去掉,还原成原始图片的 URL。

  5. 去除上传目录 URL:

    $attachment_url = str_replace( $upload_dir_paths['baseurl'] . '/', '', $url );

    这一步是为了得到相对于上传目录的附件路径。比如,如果 $urlhttp://example.com/wp-content/uploads/2023/10/image.jpg,那么 $attachment_url 就会变成 2023/10/image.jpg

  6. 数据库查询:

    $attachment_id = $wpdb->get_var( $wpdb->prepare(
    "SELECT wposts.ID
    FROM {$wpdb->posts} wposts, {$wpdb->postmeta} wpostmeta
    WHERE wpostmeta.post_id = wposts.ID
    AND wpostmeta.meta_key = '_wp_attached_file'
    AND wpostmeta.meta_value = %s
    AND wposts.post_type = 'attachment'",
    $attachment_url
    ) );

    这是最关键的一步,通过数据库查询来找到附件的 ID。 咱们来仔细看看这个 SQL 查询语句:

    • SELECT wposts.ID: 我们要查询的是 wp_posts 表(别名 wposts)的 ID 字段,也就是附件的 ID。
    • FROM {$wpdb->posts} wposts, {$wpdb->postmeta} wpostmeta: 我们要从 wp_posts 表和 wp_postmeta 表(别名 wpostmeta)中查询数据。 wp_posts 表存储了文章、页面、附件等各种类型的内容,wp_postmeta 表存储了文章、页面、附件的元数据。
    • WHERE wpostmeta.post_id = wposts.ID: 这个条件表示 wp_postmeta 表中的 post_id 字段必须等于 wp_posts 表中的 ID 字段。 也就是说,我们要查找的元数据必须属于同一个附件。
    • AND wpostmeta.meta_key = '_wp_attached_file': 这个条件表示 wp_postmeta 表中的 meta_key 字段必须等于 '_wp_attached_file''_wp_attached_file' 是一个特殊的元数据键,它存储了附件相对于上传目录的路径。
    • AND wpostmeta.meta_value = %s: 这个条件表示 wp_postmeta 表中的 meta_value 字段必须等于我们前面计算出来的 $attachment_url。 也就是说,我们要查找的附件的路径必须和我们提供的 URL 匹配。
    • AND wposts.post_type = 'attachment': 这个条件表示 wp_posts 表中的 post_type 字段必须等于 'attachment'。 也就是说,我们要查找的内容必须是附件。

    $wpdb->prepare() 函数用来预处理 SQL 查询语句,防止 SQL 注入攻击。 %s 是一个占位符,会被 $attachment_url 的值替换掉。 $wpdb->get_var() 函数用来执行 SQL 查询语句,并返回查询结果的第一行第一列的值,也就是附件的 ID。

  7. 返回结果:

    return $attachment_id;

    最后,返回查询到的附件 ID。如果查询失败,$attachment_id 的值仍然是 false

举个栗子:代码实战

光说不练假把式,咱们来写一段代码,演示一下 get_attachment_id_from_url() 的用法:

<?php
// 假设我们有这样一个 URL
$image_url = 'http://example.com/wp-content/uploads/2023/10/my-image.jpg';

// 调用 get_attachment_id_from_url() 函数
$attachment_id = get_attachment_id_from_url( $image_url );

// 判断是否找到了附件 ID
if ( $attachment_id ) {
    echo '找到了附件 ID:' . $attachment_id;
} else {
    echo '没有找到对应的附件。';
}
?>

这段代码很简单,就是把一个 URL 传给 get_attachment_id_from_url() 函数,然后根据返回值判断是否找到了附件 ID。

注意事项:一些坑需要避开

  • URL 必须是完整的: get_attachment_id_from_url() 函数需要完整的 URL 才能正常工作,包括协议 (http/https) 和域名。 如果你只提供了相对路径,它就没法找到对应的附件。
  • 上传目录配置: 如果你的 WordPress 站点的上传目录配置不正确,get_attachment_id_from_url() 函数可能无法找到附件。 确保你的 wp-config.php 文件中没有错误地定义 WP_CONTENT_DIRWP_CONTENT_URL 常量。
  • 缓存问题: 有时候,即使附件存在,get_attachment_id_from_url() 函数也可能返回 false。 这可能是因为 WordPress 的对象缓存或者数据库缓存导致了缓存不一致。 你可以尝试清除缓存来解决这个问题。
  • 多站点环境: 在多站点环境下,每个站点都有自己的上传目录。 get_attachment_id_from_url() 函数默认只能查找当前站点的附件。 如果你需要查找其他站点的附件,你需要切换到对应的站点再调用这个函数,或者修改函数代码,使其能够跨站点查找。

扩展应用:脑洞大开的用法

get_attachment_id_from_url() 函数虽然看起来很简单,但它可以用来做很多有趣的事情:

  • 图片链接替换: 你可以用它来扫描文章内容,找到所有指向你站点的图片链接,然后把它们替换成使用 wp_get_attachment_image() 函数生成的 HTML 代码,这样可以更好地控制图片的显示效果。
  • 图片统计: 你可以用它来统计文章中使用了多少张图片,哪些图片被使用的次数最多。
  • 自定义编辑器: 你可以用它来扩展 WordPress 的编辑器,让用户可以直接通过 URL 插入媒体库中的图片。
  • 数据迁移: 在进行 WordPress 数据迁移的时候,你可以用它来更新文章中的图片链接,确保它们指向新的站点。

总结:小函数,大作用

get_attachment_id_from_url() 函数是一个小巧而实用的 WordPress 函数,它可以帮助你通过 URL 找到对应的附件 ID。 理解它的源码,可以让你更好地掌握 WordPress 的工作原理,并且可以灵活地应用它来解决各种实际问题。

希望今天的讲座对你有所帮助! 下次有机会再跟大家分享其他有趣的 WordPress 技巧。 谢谢大家!

发表回复

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