各位观众,晚上好!我是今晚的讲师,咱们今天来聊聊 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/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 是否属于我们站点的媒体库。 -
判断 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 技巧。 谢谢大家!