各位观众,晚上好!我是今晚的讲师,咱们今天来聊聊 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;
}
看起来代码不少,但别怕,咱们一步步拆解:
-
参数校验:
if ( '' === $url ) { return false; }这个很简单,如果传进来的 URL 是空的,那就直接返回
false,没啥好查的。 -
获取上传目录信息:
$upload_dir_paths = wp_upload_dir();wp_upload_dir()函数会返回一个数组,里面包含了你的 WordPress 站点上传目录的相关信息,比如:键名 含义 示例 path上传目录的物理路径 /var/www/html/wp-content/uploadsurl上传目录的 URL http://example.com/wp-content/uploadssubdir当前年月子目录 /2023/10basedir上传目录的物理路径 (和 path一样)/var/www/html/wp-content/uploadsbaseurl上传目录的 URL (和 url一样)http://example.com/wp-content/uploadserror是否有错误,通常是布尔值 false或错误消息字符串。false我们主要用到的是
baseurl,也就是上传目录的 URL,用来判断传进来的 URL 是否属于我们站点的媒体库。 -
判断 URL 是否属于本站:
if ( false !== strpos( $url, $upload_dir_paths['baseurl'] ) ) { // ... }strpos()函数用来查找一个字符串在另一个字符串中首次出现的位置。如果$url中包含$upload_dir_paths['baseurl'],说明这个 URL 很可能就是我们站点的附件 URL。 -
处理缩略图 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。 -
去除上传目录 URL:
$attachment_url = str_replace( $upload_dir_paths['baseurl'] . '/', '', $url );这一步是为了得到相对于上传目录的附件路径。比如,如果
$url是http://example.com/wp-content/uploads/2023/10/image.jpg,那么$attachment_url就会变成2023/10/image.jpg。 -
数据库查询:
$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。 -
返回结果:
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_DIR和WP_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 技巧。 谢谢大家!