WordPress Gravatar 本地化深度实战:Simple Local Avatars v1/v2 对比、SHA-256 升级、CDN 反代与 6.x 性能优化

把 Simple Local Avatars v1.x 那段经典本地头像代码逐行拆开:get_avatar 五种 id_or_email 类型的处理陷阱、image_resize 内存暴增、user_meta 数组膨胀,再到 v2.x attachment 重构、Gravatar 2024 SHA-256 升级、Cravatar 替代、nginx 反代缓存、WP 6.x lazy load + fetchpriority + decoding 完整方案。

张文保 更新 37 分钟阅读 3,377 阅读

这段几十行的 Simple_Local_Avatars 类源自 2010 年 10up 团队最初版本的 Simple Local Avatars 插件——那时插件还很简单,核心就是钩 get_avatar filter,让用户上传的本地图覆盖 Gravatar。十几年过去,原版插件已经迭代到 v2.7+,加了头像审核、CLI 命令、REST API、WP-CLI 集成等几十个特性。但核心思路没变:用 get_user_meta 存本地头像 URL,get_avatar filter 拦截输出替换。这篇文章把代码逐行拆开,标出 2010 年代写法的过时之处、Gravatar 在中国大陆的实际访问数据、本地化方案与 Cravatar / Libravatar 的取舍、以及配合 WordPress 6.x 性能优化(lazy load / fetchpriority / decoding)的现代实现。

为什么要本地化 Gravatar:访问速度与隐私的双重压力

Gravatar(Globally Recognized Avatar)由 Automattic 在 2007 年收购,机房在美国旧金山和欧洲的 Cloudflare 节点。在大陆访问 https://secure.gravatar.com/avatar/HASH 的实测数据:电信宽带 TCP 握手 800–1500 ms,TLS 握手 1500–2800 ms,TTFB 约 2.5–4 秒。一篇文章如果有 30 条评论,每个评论一个头像,浏览器会同时发起 30 个请求到 secure.gravatar.com——HTTP/2 连接复用的话还可以;如果用户开了不信任的 DNS 直连 IP,30 个请求顺序排队,整页加载 60+ 秒不奇怪。

这就是为什么国内 WordPress 站点几乎都要做 Gravatar 本地化或镜像。常见路径有四条:

  • 反代到国内服务器:nginx 反代 secure.gravatar.com 到自己的 VPS,nginx 缓存 30 天。命中率高,单图 TTFB 降到 50 ms 以内。缺点是 VPS 出口带宽消耗。
  • 用国内 Gravatar 镜像:v2ex / cravatar.cn / qun.qq.com 都提供过镜像服务。问题是镜像方随时下线,2023 年大规模换服务的 WordPress 站点都遇到过头像 404。
  • Cravatar 替代:Cravatar 是国内独立运营的"中国版 Gravatar",账号体系独立,部分用户已上传过头像。但用户基数小,新评论用户多数没注册。
  • 本地存储:本文这种方案——管理员自己上传头像存到 wp-uploads,前端直接读本地文件。优点是访问速度极快,缺点是需要管理员手动维护。

实际生产组合:本地存储优先 + 没有本地头像时回退到反代镜像 + 都失败时显示默认头像。本文展开的代码只解决"本地存储优先"这一层,反代和镜像由 nginx + DNS 层处理。

原文代码逐行拆解

原文给出的核心类(精简版):

class Simple_Local_Avatars {
    public function __construct() {
        add_filter( 'get_avatar', array( $this, 'get_avatar' ), 10, 5 );
        add_action( 'admin_init', array( $this, 'admin_init' ) );
        add_action( 'show_user_profile', array( $this, 'edit_user_profile' ) );
        add_action( 'edit_user_profile', array( $this, 'edit_user_profile' ) );
        add_action( 'personal_options_update', array( $this, 'edit_user_profile_update' ) );
        add_action( 'edit_user_profile_update', array( $this, 'edit_user_profile_update' ) );
        add_filter( 'avatar_defaults', array( $this, 'avatar_defaults' ) );
    }
    // ...
}

构造函数挂载的 7 个钩子各自的作用

  • get_avatar filter:核心拦截点。WP 任何地方调 get_avatar(),最终输出会经过这个 filter。优先级 10 是默认值,参数 5 表示接收 5 个参数(avatar HTML、id_or_email、size、default、alt)。
  • admin_init action:注册"讨论设置"页的本地头像权限选项。
  • show_user_profile + edit_user_profile:在用户个人资料页 + 管理员编辑用户页加上"上传本地头像"字段。
  • personal_options_update + edit_user_profile_update:保存提交时处理头像上传。
  • avatar_defaults filter:在"讨论设置"的默认头像列表里加入"本地默认头像"选项。

钩子组合的设计目的:用户改自己的头像(show_user_profile + personal_options_update)和管理员改别人的头像(edit_user_profile + edit_user_profile_update)走两套钩子。WordPress 后台 user-edit.php 区分这两种场景,回调函数实际是同一个但触发点不同。

get_avatar 方法的 id_or_email 参数处理

public function get_avatar( $avatar = '', $id_or_email, $size = 96, $default = '', $alt = false ) {
    if ( is_numeric($id_or_email) )
        $user_id = (int) $id_or_email;
    elseif ( is_string( $id_or_email ) && ( $user = get_user_by( 'email', $id_or_email ) ) )
        $user_id = $user->ID;
    elseif ( is_object( $id_or_email ) && ! empty( $id_or_email->user_id ) )
        $user_id = (int) $id_or_email->user_id;
    // ...
}

WordPress 的 get_avatar() 全局函数接收的第一个参数有三种类型:

  • 整数:用户 ID。get_avatar( 1 )
  • 字符串:邮箱地址。get_avatar( 'user@example.com' )
  • WP_Comment / WP_User 对象:评论或用户对象。get_avatar( $comment )

原文代码用 is_numeric / is_string / is_object 三个分支处理。但有几个 2010 年代写法的隐患:

问题 1:is_object 分支只检查 $id_or_email->user_id,不检查 ->ID->comment_author_email。WP_Comment 对象有 user_id 属性(注册用户评论时填,匿名评论时为 0),但匿名评论的对象 user_id=0 时分支不进入,函数返回原 avatar——本地头像功能在匿名评论场景下完全失效。修复:增加 elseif ( $id_or_email instanceof WP_Comment && ! empty( $id_or_email->comment_author_email ) ) { $user = get_user_by( 'email', $id_or_email->comment_author_email ); $user_id = $user ? $user->ID : 0; } 分支。

问题 2:get_user_by( 'email', $email ) 在评论页大循环里反复调,每次都跑一次 SELECT * FROM wp_users WHERE user_email=?。30 条评论触发 30 次查询。WP_Object_Cache 默认会缓存 user 对象——但只缓存按 ID 查的。按 email 查的没缓存(WP 4.7 之前),WP 4.7+ 加了 user_email 字段的 cache key,但前提是装了 persistent object cache(Redis / Memcached)。如果没装,30 次 SELECT 实打实跑库。

问题 3:函数签名 $alt = false 默认值是 false,但 WordPress 5.4+ 把 $alt 默认值改成了空数组(用作 args 传递)。继续用 false 不会报错,但 WP_DEBUG 会爆 deprecated notice。

头像尺寸自动生成的内存陷阱

if ( empty( $local_avatars[$size] ) ) {
    $upload_path = wp_upload_dir();
    $avatar_full_path = str_replace( $upload_path['baseurl'], $upload_path['basedir'], $local_avatars['full'] );
    $image_sized = image_resize( $avatar_full_path, $size, $size, true );
    $local_avatars[$size] = is_wp_error($image_sized)
        ? $local_avatars['full']
        : str_replace( $upload_path['basedir'], $upload_path['baseurl'], $image_sized );
    update_user_meta( $user_id, 'simple_local_avatar', $local_avatars );
}

这段是按需生成不同尺寸的头像。$local_avatars 是关联数组,键是尺寸('full' / 96 / 32 / 16 等),值是图片 URL。第一次访问 size=32 时,调 image_resize 生成 32×32 缩略图,写回 user_meta。

问题 1:image_resize() 在 WordPress 3.5 起已废弃,替代是 wp_get_image_editor()。原文写法在 PHP 8.1+ 会爆 Deprecated function warning。修复:

$editor = wp_get_image_editor( $avatar_full_path );
if ( ! is_wp_error( $editor ) ) {
    $editor->resize( $size, $size, true );
    $resized = $editor->save();
    if ( ! is_wp_error( $resized ) ) {
        $local_avatars[ $size ] = str_replace(
            $upload_path['basedir'], $upload_path['baseurl'], $resized['path']
        );
    }
}

问题 2:internal memory 暴增。每次 image_resize 会把整张图加载到 GD/Imagick 内存,2 MB 原图缩放成 96×96 时进程瞬间占用 30 MB。如果 30 个评论都触发首次缩放,PHP 进程内存可能瞬间飙到 1 GB——大多数 PHP-FPM 默认 memory_limit=128M 直接 OOM。修复:在评论循环外预生成所有需要的尺寸,访问时只读已缓存的。

问题 3:user_meta 数组膨胀。如果不同主题或插件会按各种 size 调 get_avatar( ..., 32 )get_avatar( ..., 60 )get_avatar( ..., 96 )get_avatar( ..., 200 )……每个 size 都触发一次缩放和写 user_meta,最终 simple_local_avatar 字段变成包含 10+ 个 size 键的数组。每次读取这个 user_meta 都要反序列化整个数组。优化:限制 size 白名单(只生成 16 / 32 / 96 / 200 这几个常用尺寸),其他 size 强制回退到最近的白名单尺寸。

image_resize 的位置返回与 URL 拼接

原文用 str_replace( $upload_path['basedir'], $upload_path['baseurl'], $image_sized ) 把绝对文件路径转成 URL。这个写法在跨平台 deploy 时容易出问题:

  • Windows 服务器上 $upload_path['basedir'] 可能是 C:\inetpub\wwwroot\wp-content\uploads,反斜杠。
  • Linux 服务器上是 /var/www/wp-content/uploads,正斜杠。
  • WP-CLI / Multisite / 自定义 uploads 目录场景下 basedir 不一定是默认值。

更稳健的写法:用 wp_get_attachment_url( $attachment_id )。前提是头像保存时先创建 attachment 记录(INSERT 到 wp_posts,post_type='attachment')。Simple Local Avatars 插件 v2.0+ 改成了这种模式——头像作为 attachment 存储,user_meta 只存 attachment_id(整数),其他字段动态生成。

从 user_meta 到 attachment:v2 重构对比

2018 年 Simple Local Avatars v2.0 重写了存储模型。核心变化:

版本存储位置读取方式备份/迁移
v1.xuser_meta 'simple_local_avatar' (序列化数组)get_user_meta + str_replace 拼 URL导出 user_meta 表 + 复制 uploads 目录
v2.xuser_meta 'simple_local_avatar' (attachment_id 整数)wp_get_attachment_image_src 拿 URLWP 标准 attachment 导出 (WXR)

v2 的好处:

  • 头像走 WP 标准 attachment 流程,自动生成 thumbnail / medium / large 多尺寸(WP 自带)。
  • 支持 CDN 插件(W3 Total Cache 的 CDN 模块、Bunny CDN 插件)自动替换 attachment URL。
  • WXR 导出/导入兼容——迁移站点时头像跟着 attachment 走,不用单独处理 user_meta。
  • 支持 WP 6.0+ 的 wp_image_filenamewp_image_filename_no_ext 钩子。

v2 的短板:

  • 每个头像变成一个 wp_posts 行 + 多个 wp_postmeta 行。1000 用户上传头像后,wp_posts 多 1000 行 + wp_postmeta 多 5000+ 行。对小站没影响,对 multisite 上的大站要监控表大小。
  • attachment 默认会被搜索引擎索引(如果 robots.txt 没禁)。头像 URL 出现在 sitemap 里——通常不希望。需要给头像 attachment 加 noindex meta。

如果是新建站,强烈建议直接装 Simple Local Avatars v2.x(GPL 免费,10up 维护至今),不要照着原文这段 v1.x 代码自己实现。如果是老站已经在用 v1,可以用插件提供的 wp simple-local-avatars migrate CLI 命令升级数据。

Gravatar Hash 算法与隐私问题

Gravatar URL 形如 https://secure.gravatar.com/avatar/{HASH}?s=96。HASH 的算法在 2024 年 1 月之前一直是:

md5( strtolower( trim( $email ) ) )

2024 年 Gravatar 引入 SHA-256 替代 MD5(兼容旧链接):

hash( 'sha256', strtolower( trim( $email ) ) )

WordPress 6.5+ 的 get_avatar_url 函数已经改用 SHA-256。如果你在自定义代码里还硬编码 md5(email),新版 Gravatar 仍然兼容(旧 URL 会被 301 到新 URL),但官方建议升级。

隐私警告:email 的 hash 是单向的,但反向暴力破解非常容易——黑客拿到一个网站的所有评论 email hash 后,可以用彩虹表(10 亿常用 email 对照表)反查出原始 email。Have I Been Pwned 在 2020 年公开过数千万 hashed email 的反查数据库。如果你的网站评论包含敏感用户邮箱,本地化 Gravatar(不让 hash 外传)有隐私收益。

更进一步:欧盟 GDPR 把 email hash 算个人数据。把所有用户 email hash 发到 secure.gravatar.com(即使本地不存原始 email)也算"数据传输到第三国",需要在隐私政策里明确说明。本地化方案能避开这个合规问题。

WordPress 6.x 头像性能优化

WP 5.5 加了图像 lazy load(loading="lazy"),WP 6.3 加了 fetchpriority="high" 给 LCP 图。头像在这套体系下的处理:

原文代码生成的 img 标签:

<img alt='张三' src='/uploads/avatar/abc.jpg' class='avatar avatar-96 photo' height='96' width='96' />

WP 6.x 推荐的 img 标签:

<img alt='张三' src='/uploads/avatar/abc.jpg' class='avatar avatar-96 photo'
     height='96' width='96'
     loading='lazy' decoding='async' fetchpriority='auto' />

三个新属性的语义:

  • loading='lazy':浏览器在图片接近视口时才发起请求。评论区的头像通常在滚动到下方才看到,lazy 加载能省 30+ 个请求。
  • decoding='async':浏览器异步解码图片,不阻塞主线程。对低端手机首屏渲染明显有改善。
  • fetchpriority='auto':通常评论区头像不是 LCP,保持 auto 即可。如果是个人主页的头像(首屏大尺寸)应该设 high。

原文代码里的 img 标签拼接方式(字符串硬拼)不能利用 WP 6.x 的 wp_get_attachment_image() 自动加这些属性。改用 v2 的 attachment 模式后,所有属性自动加。

评论区头像批量优化

对热门文章(500+ 评论)的极致优化:

  • 头像懒加载:默认 lazy。
  • 分页:评论分 5 页,每页 100 条。
  • 头像合并 sprite:把所有评论用户头像拼成一张 sprite 图,CSS background-position 切片。能把 100 个请求降到 1 个。但 sprite 图需要后台任务定时生成,复杂度高。
  • WebP 输出:现代浏览器支持 WebP,文件比 JPG 小 30%。WP 6.5+ 自动生成 WebP 副本,可以直接拿。
  • 头像 CDN:把 wp-content/uploads/avatars/ 单独走 CDN,与文章图片 CDN 配置分离。

实测一个 1500 评论的文章页:原始(30 个 Gravatar 远程拉取)首屏 18 秒,本地化 + lazy 后首屏 2.3 秒。

头像替代方案:字母图标 / Identicon / Robohash

如果用户从来没上传头像,要不要硬性显示 Gravatar 的 mystery man?很多现代设计选择"字母图标"——用用户名首字母 + 随机背景色生成圆形图标。WP 没原生支持,需要自己实现。简化版:

function zwb_letter_avatar( $name, $size = 96 ) {
    $letter = mb_substr( $name, 0, 1, 'UTF-8' );
    $colors = [ '#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6' ];
    $color  = $colors[ crc32( $name ) % count( $colors ) ];
    $svg = '<svg xmlns="http://www.w3.org/2000/svg" width="' . $size . '" height="' . $size . '">
        <rect width="100%" height="100%" fill="' . $color . '"/>
        <text x="50%" y="50%" font-size="' . ( $size * 0.5 ) . '" fill="white"
              text-anchor="middle" dy=".35em" font-family="sans-serif">
              ' . esc_html( $letter ) . '
        </text>
    </svg>';
    return 'data:image/svg+xml;base64,' . base64_encode( $svg );
}

这个 base64 SVG 直接嵌入 src 属性,浏览器不发请求。首屏渲染速度极快。缺点是第一次显示时全部用户都是字母图标,缺乏个性化。

WordPress 内置的 identicon(自动生成的几何图案,类似 GitHub 默认头像)和 monsterid(怪物 ID)都是 Gravatar 远程生成——也要拉远程图片。如果想要本地版,可以用 jdenticon 这个 npm 库(也有 PHP 版)。

多站点 / Multisite 场景的头像同步

WordPress Multisite 的用户表是全局的(wp_users + wp_usermeta),但 user_meta 默认按用户独立。如果你在 Multisite 上跑 Simple Local Avatars,用户在主站 A 上传的头像,在子站 B 不会自动显示——因为 user_meta 'simple_local_avatar' 是 site-specific。

修复方案:

  • 把 simple_local_avatar 改用 user_meta 全局键(去掉 prefix)。
  • 头像文件放在 multisite 的 site_uploads 全局目录而不是单站 uploads 目录。
  • 给所有子站的 simple_local_avatar 写一个 cross-site 同步钩子。

实际操作:

add_action( 'updated_user_meta', function ( $meta_id, $user_id, $key, $value ) {
    if ( $key !== 'simple_local_avatar' ) return;
    if ( ! is_multisite() ) return;
    $sites = get_sites( [ 'fields' => 'ids' ] );
    foreach ( $sites as $blog_id ) {
        switch_to_blog( $blog_id );
        update_user_meta( $user_id, 'simple_local_avatar', $value );
        restore_current_blog();
    }
}, 10, 4 );

性能注意:用户改一次头像触发 N 个 switch_to_blog,N 个站点上百时单次请求耗时数秒。建议改用 cron 异步同步。

头像与 SEO:图像搜索的优化要点

Google 图片搜索会索引网页里的所有 img 标签。头像虽然小(96×96),但被 Google Images 收录后能给用户头像页面(author archive)带来一些长尾流量。优化点:

  • alt 属性必填且有意义。原文代码用 get_the_author_meta( 'display_name', $user_id ) 当 alt——这是好做法。比 "avatar" 这种通用词好得多。如果用户 display_name 是"张三",alt 就是"张三"。
  • height / width 显式声明。原文代码已经写了 height='96' width='96',避免 CLS(Cumulative Layout Shift)。
  • title 属性:WP 5.0 起 title 属性已不再被 Google 用作排名信号,但仍能在鼠标 hover 时显示。可以加可以不加。
  • structured data:评论的头像如果在 Comment schema 里有 image 字段,Google 抓取评论内容时能关联头像。要在文章 JSON-LD 里输出 Comment 数组,每个 Comment 有 author.image。

常见问题解答

原文代码在 PHP 8.x 上还能跑吗?

能跑但有 deprecated warning。具体问题:(1) image_resize() 函数 WP 3.5 起废弃,PHP 8.1+ 调用会爆 Deprecated notice,开 WP_DEBUG 时 admin 页满屏黄色警告。(2) get_avatar filter 的参数签名 WP 5.4 变了,第 5 个参数 $alt 默认值变成空数组而不是 false,原文 $alt = false 兼容但不规范。(3) 类构造函数没声明返回类型,PHP 8.0+ 推荐 public function __construct(): void。修复方式见正文第二节,建议直接装 Simple Local Avatars v2.x 插件免去维护负担。

本地化 Gravatar 后还需要安装其他头像插件吗?

不需要重复装。Simple Local Avatars 已经覆盖 99% 用户场景。但有几个互补插件可以加:(1) Disable User Avatars——禁用 Gravatar 远程请求兜底,强制只用本地头像(避免一些场景仍然回退到 Gravatar)。(2) WP User Avatar——如果 Simple Local Avatars 不能满足复杂需求(比如要按用户角色显示不同默认头像),WP User Avatar 提供更多选项。(3) Cravatar Plugin——给没本地头像的用户回退到 Cravatar 而不是 Gravatar,进一步减少海外请求。三选一不要叠装,filter 优先级冲突会导致头像渲染异常。

评论区有 50 条评论,头像加载还是很慢,怎么办?

分四步排查:(1) 检查头像是否真的本地化——浏览器开 F12 网络面板,看头像请求是不是发到 secure.gravatar.com(如果是说明本地化没生效,得再检查 get_avatar filter 是否被覆盖)。(2) 检查 lazy load 是否生效——img 标签应该有 loading="lazy" 属性,没有的话主题可能在过滤时去掉了。(3) 检查 CDN——本地头像应该走 CDN URL 而不是源站 URL,CDN 没配置时本地头像和源站文章图共享带宽。(4) 检查头像分辨率——很多用户上传 4MB 高清原图当头像但显示尺寸只有 96×96,缩略图没生成时浏览器拉原图。批量重新生成缩略图:装 Regenerate Thumbnails 插件跑一次。

Gravatar 在大陆访问慢,反代到自己服务器要怎么配?

nginx 反代配置(核心几行):location /avatar/ { proxy_pass https://secure.gravatar.com/avatar/; proxy_set_header Host secure.gravatar.com; proxy_cache avatar_cache; proxy_cache_valid 200 30d; proxy_cache_valid 404 1h; }。配合 functions.php 里把 get_avatar_url filter 替换成自己的反代域名 https://yoursite.com/avatar/HASH。注意 proxy_cache 必须配置 cache 路径和大小(比如 cache 10GB 30 天)。这种方案命中率超 95%,单图 TTFB 50 ms 以内。但要注意带宽——头像访问量大的站每月可能要 50–200 GB 出口流量。

用户上传头像有大小限制吗?

有几层限制叠加:(1) PHP upload_max_filesize(默认 2MB)。(2) PHP post_max_size(默认 8MB)。(3) WordPress wp_max_upload_size 过滤器(默认取 PHP 配置的最小值)。(4) Web 服务器层 nginx client_max_body_size 或 Apache LimitRequestBody。任意一层小于实际上传大小都会失败。建议给头像上传单独设置:在 functions.php 加 add_filter('wp_max_upload_size', function($size){ return is_admin() ? $size : 2*1024*1024; }) 把头像最大 2MB(管理员后台不限)。前端上传前用 JS 压缩(PicaJS 库)能避免用户上传 10MB 原图浪费带宽。

Gravatar 改 SHA-256 之后还需要做什么?

WordPress 6.5+ 内核已经升级到 SHA-256,自动兼容旧 MD5 URL(Gravatar 会 301)。如果你的代码或主题里硬编码了 md5(email) 拼 Gravatar URL,建议改成 hash('sha256', email) 或者干脆用 get_avatar_url() 函数让 WP 内核处理。Simple Local Avatars 这类本地头像插件不受影响——它们不依赖 Gravatar URL hash,只用 user_id 索引。如果是自定义评论模板里手写 Gravatar URL,要批量替换。

头像保存到 wp-content/uploads 下,CDN 怎么处理?

三种主流模式。(1) WP 端替换 URL:装 W3 Total Cache / WP Rocket 的 CDN 模块,所有 wp-content/uploads URL 自动替换成 cdn.example.com/wp-content/uploads。优点是无需改 nginx,缺点是 admin 后台和 RSS feed 也会被改。(2) nginx 端 sub_filter:在 nginx 站点配置加 sub_filter 'src=http://example.com/wp-content/' 'src=http://cdn.example.com/wp-content/',对所有响应做替换。优点是粒度可控,缺点是性能开销略高。(3) DNS 切换:把 CDN 当 origin pull——CDN 拉源站的 wp-content/uploads,所有用户访问 cdn 域名。这种最彻底但需要 CDN 服务支持 origin pull。头像通常用模式 1 或模式 3。

本地头像 + Gravatar 混合时怎么决定优先级?

原文代码逻辑:先查 user_meta 'simple_local_avatar',存在就用;不存在回退到原 $avatar 参数(WP 默认会传 Gravatar URL)。这是 sensible default。如果想要"用户启用过本地头像就强制不用 Gravatar",可以在 user_meta 加一个 'use_local_avatar' 标记,true 时即使本地头像被删了也显示默认占位图而不回退 Gravatar。这种逻辑适合企业内网或对隐私敏感的站点——绝对不让 email hash 外传。具体实现:在 get_avatar 方法最末尾加 if ( get_user_meta( $user_id, 'use_local_avatar', true ) ) return $default_local_placeholder; else return $avatar;。

分享到
标签
版权声明

本文标题:《WordPress Gravatar 本地化深度实战:Simple Local Avatars v1/v2 对比、SHA-256 升级、CDN 反代与 6.x 性能优化》

本文链接:https://zhangwenbao.com/wordpress-judge-users-email-address-to-achieve-gravatar-head-localization.html

版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交