李锋镝的博客

  • 首页
  • 时间轴
  • 评论区显眼包🔥
  • 左邻右舍
  • 博友圈
  • 关于我
    • 关于我
    • 另一个网站
    • 我的导航站
    • 网站地图
    • 赞助
  • 留言
  • 🚇开往
Destiny
自是人生长恨水长东
  1. 首页
  2. 原创
  3. WP插件
  4. 正文

WordPress评论添加UserAgent以及地理位置信息

2025年12月1日 353点热度 1人点赞 3条评论

看到大家的博客评论下面都带有UserAgent信息,还有地理位置信息,一直都很羡慕,奈何网上苦寻插件不见。要么就是插件年久失修,要么就是直接主题集成,无一我想要的。昨天又想起这事,心痒难耐,遂决定自己敲一个出来(其实是AI代笔)。

首先看了下WordPress的评论表,发现UserAgent和用户IP都存的有,这就很方便了,至少数据不需要自己另外新建表保存了。

解析UA信息

解析UserAgent其实很简单,代码如下:

function parse_user_agent( $u_agent = null ) {
    if( $u_agent === null && isset($_SERVER['HTTP_USER_AGENT']) ) {
        $u_agent = $_SERVER['HTTP_USER_AGENT'];
    }

    if( $u_agent === null ) {
        throw new \InvalidArgumentException('parse_user_agent requires a user agent');
    }

    $platform = null;
    $browser  = null;
    $version  = null;

    $empty = array( 'platform' => $platform, 'browser' => $browser, 'version' => $version );

    if( !$u_agent ) {
        return $empty;
    }

    if( preg_match('/\((.*?)\)/m', $u_agent, $parent_matches) ) {
        preg_match_all('/(?P<platform>BB\d+;|Android|CrOS|Tizen|iPhone|iPad|iPod|Linux|(Open|Net|Free)BSD|Macintosh|Windows(\ Phone)?|Silk|linux-gnu|BlackBerry|PlayBook|X11|(New\ )?Nintendo\ (WiiU?|3?DS|Switch)|Xbox(\ One)?)
                (?:\ [^;]*)?
                (?:;|$)/imx', $parent_matches[1], $result);

        $priority = array( 'Xbox One', 'Xbox', 'Windows Phone', 'Tizen', 'Android', 'FreeBSD', 'NetBSD', 'OpenBSD', 'CrOS', 'X11' );

        $result['platform'] = array_unique($result['platform']);
        if( count($result['platform']) > 1 ) {
            if( $keys = array_intersect($priority, $result['platform']) ) {
                $platform = reset($keys);
            } else {
                $platform = $result['platform'][0];
            }
        } elseif( isset($result['platform'][0]) ) {
            $platform = $result['platform'][0];
        }
    }

    if( $platform == 'linux-gnu' || $platform == 'X11' ) {
        $platform = 'Linux';
    } elseif( $platform == 'CrOS' ) {
        $platform = 'Chrome OS';
    }

    preg_match_all('%(?P<browser>Camino|Kindle(\ Fire)?|Firefox|Iceweasel|IceCat|Safari|MSIE|Trident|AppleWebKit|
                TizenBrowser|(?:Headless)?Chrome|YaBrowser|Vivaldi|IEMobile|Opera|OPR|Silk|Midori|Edge|Edg|CriOS|UCBrowser|Puffin|OculusBrowser|SamsungBrowser|
                Baiduspider|Googlebot|YandexBot|bingbot|Lynx|Version|Wget|curl|
                Valve\ Steam\ Tenfoot|
                NintendoBrowser|PLAYSTATION\ (\d|Vita)+)
                (?:\)?;?)
                (?:(?:[:/ ])(?P<version>[0-9A-Z.]+)|/(?:[A-Z]*))%ix',
        $u_agent, $result);

    // If nothing matched, return null (to avoid undefined index errors)
    if( !isset($result['browser'][0]) || !isset($result['version'][0]) ) {
        if( preg_match('%^(?!Mozilla)(?P<browser>[A-Z0-9\-]+)(/(?P<version>[0-9A-Z.]+))?%ix', $u_agent, $result) ) {
            return array( 'platform' => $platform ?: null, 'browser' => $result['browser'], 'version' => isset($result['version']) ? $result['version'] ?: null : null );
        }

        return $empty;
    }

    if( preg_match('/rv:(?P<version>[0-9A-Z.]+)/i', $u_agent, $rv_result) ) {
        $rv_result = $rv_result['version'];
    }

    $browser = $result['browser'][0];
    $version = $result['version'][0];

    $lowerBrowser = array_map('strtolower', $result['browser']);

    $find = function ( $search, &$key = null, &$value = null ) use ( $lowerBrowser ) {
        $search = (array)$search;

        foreach( $search as $val ) {
            $xkey = array_search(strtolower($val), $lowerBrowser);
            if( $xkey !== false ) {
                $value = $val;
                $key   = $xkey;

                return true;
            }
        }

        return false;
    };

    $findT = function ( array $search, &$key = null, &$value = null ) use ( $find ) {
        $value2 = null;
        if( $find(array_keys($search), $key, $value2) ) {
            $value = $search[$value2];

            return true;
        }

        return false;
    };

    $key = 0;
    $val = '';
    if( $findT(array( 'OPR' => 'Opera', 'UCBrowser' => 'UC Browser', 'YaBrowser' => 'Yandex', 'Iceweasel' => 'Firefox', 'Icecat' => 'Firefox', 'CriOS' => 'Chrome', 'Edg' => 'Edge' ), $key, $browser) ) {
        $version = $result['version'][$key];
    }elseif( $find('Playstation Vita', $key, $platform) ) {
        $platform = 'PlayStation Vita';
        $browser  = 'Browser';
    } elseif( $find(array( 'Kindle Fire', 'Silk' ), $key, $val) ) {
        $browser  = $val == 'Silk' ? 'Silk' : 'Kindle';
        $platform = 'Kindle Fire';
        if( !($version = $result['version'][$key]) || !is_numeric($version[0]) ) {
            $version = $result['version'][array_search('Version', $result['browser'])];
        }
    } elseif( $find('NintendoBrowser', $key) || $platform == 'Nintendo 3DS' ) {
        $browser = 'NintendoBrowser';
        $version = $result['version'][$key];
    } elseif( $find('Kindle', $key, $platform) ) {
        $browser = $result['browser'][$key];
        $version = $result['version'][$key];
    } elseif( $find('Opera', $key, $browser) ) {
        $find('Version', $key);
        $version = $result['version'][$key];
    } elseif( $find('Puffin', $key, $browser) ) {
        $version = $result['version'][$key];
        if( strlen($version) > 3 ) {
            $part = substr($version, -2);
            if( ctype_upper($part) ) {
                $version = substr($version, 0, -2);

                $flags = array( 'IP' => 'iPhone', 'IT' => 'iPad', 'AP' => 'Android', 'AT' => 'Android', 'WP' => 'Windows Phone', 'WT' => 'Windows' );
                if( isset($flags[$part]) ) {
                    $platform = $flags[$part];
                }
            }
        }
    } elseif( $find(array( 'IEMobile', 'Edge', 'Midori', 'Vivaldi', 'OculusBrowser', 'SamsungBrowser', 'Valve Steam Tenfoot', 'Chrome', 'HeadlessChrome' ), $key, $browser) ) {
        $version = $result['version'][$key];
    } elseif( $rv_result && $find('Trident') ) {
        $browser = 'MSIE';
        $version = $rv_result;
    } elseif( $browser == 'AppleWebKit' ) {
        if( $platform == 'Android' ) {
            $browser = 'Android Browser';
        } elseif( strpos($platform, 'BB') === 0 ) {
            $browser  = 'BlackBerry Browser';
            $platform = 'BlackBerry';
        } elseif( $platform == 'BlackBerry' || $platform == 'PlayBook' ) {
            $browser = 'BlackBerry Browser';
        } else {
            $find('Safari', $key, $browser) || $find('TizenBrowser', $key, $browser);
        }

        $find('Version', $key);
        $version = $result['version'][$key];
    } elseif( $pKey = preg_grep('/playstation \d/i', $result['browser']) ) {
        $pKey = reset($pKey);

        $platform = 'PlayStation ' . preg_replace('/\D/', '', $pKey);
        $browser  = 'NetFront';
    }

    return array( 'platform' => $platform ?: null, 'browser' => $browser ?: null, 'version' => $version ?: null );
}

解析IP

另外一个难题是根据用户IP解析地址位置信息,网上大多都是调用Api接口或者IP库来解析的。

首先说说Api,大部分Api都有请求频率的限制,太频繁容易被限制,要么就是付费接口,那更不行,那Api这条路子基本行不通了。

第二条路子就是IP库,这个办法挺好的,但是需要经常更新IP库,不然时间久了位置信息可能不准了,不准的话,还不如不要,咱要做就要做好不是。这样的话,还得写个定时任务去更新IP库,挺麻烦的,我就想要一个轻量级的小功能而已,几行代码实现就行。

后来灵机一动,想起了自己在用的另外一款插件WP Statistics,这个插件自带的就有IP库,而且它自己还会定时去更新IP库,简直完美,那要怎么用它呢?这个简单,看它代码就行了。

经过研究,找到了GeolocationFactory这个类,里面有个函数叫getLocation,传入IP就能返回位置信息,完美!

$location_data = WP_Statistics\Service\Geolocation\GeolocationFactory::getLocation($comment_ip);
$country = $location_data['country'] ?? '未知';
$region = $location_data['region'] ?? '未知';
$city = $location_data['city'] ?? '未知';

渲染页面

剩下的就是将解析的信息渲染到页面上,WordPress提供有现成的钩子,调用即可。

function wpcdi_add_info_after_comment_content($comment_text, $comment) {
    // 仅前台显示,且评论已通过审核
    if (is_admin() || $comment->comment_approved != 1) {
        return $comment_text;
    }
    $location_icon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 20 20" fill="#e9ecef" stroke="#495057" stroke-width="2">
          <path d="M12 3c-2.5 0-4.5 2-4.5 4.5c0 2.5 2 5.5 4.5 9c2.5-3.5 4.5-6.5 4.5-9C16.5 5 14.5 3 12 3z"/>
          <circle cx="12" cy="7.5" r="1.5" fill="#495057"/>
        </svg>';

    // 获取评论者IP、UA并过滤
    $comment_ip = sanitize_text_field($comment->comment_author_IP);
    $user_agent = sanitize_text_field($comment->comment_agent);

    // 获取设备/地理信息
    $info = wpcdi_get_comment_info($comment_ip, $user_agent);

    $browser_icon = wpcdi_get_svg_icon('browser', $info['browser']);
    $os_icon = wpcdi_get_svg_icon('os', $info['os']);

    $info_html = '<div class="wpcdi-comment-info" style="padding-top:10px; font-size: 12px; color: #495057; line-height: 1.5; display: flex; align-items: center; gap: 5px;">
    <span style="display: inline-flex; align-items: center;">' . $os_icon . '<span>' . esc_html($info['os']) . '</span></span>
    <span style="display: inline-flex; align-items: center;">' . $browser_icon . '<span>' . esc_html($info['browser']) . '</span></span><span style="display: inline-flex; align-items: center;">' . $location_icon . '<span>'  . esc_html($info['location']) . '</span></span></div>';
    // 追加到评论内容后方
    return $comment_text . $info_html;
}
// 绑定评论内容钩子,优先级10,参数2
add_filter('comment_text', 'wpcdi_add_info_after_comment_content', 10, 2);

大家可以评论一下看看效果,或者有什么好的点子也可以说一下。

除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.lifengdi.com/article/cha-jian/4606

相关文章

  • WordPress实现用户评论等级排行榜插件
  • 做了一个WordPress文章热力图插件
  • Dylan Custom Plugin 1.0.3版本更新说明
  • Dylan Custom Plugin 1.0.2版本更新说明
  • 我的第一个WordPress插件:Dylan Custom Plugin上线了
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: UserAgent WordPress 评论
最后更新:2025年12月1日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >

文章评论

  • yourlai黑铁

    学到了学到了,大佬牛的

    Windows
    Edge 145.0.0.0 中国-山东-淄博
    2026年2月23日
    回复
  • 鲁迅黑铁

    再看一下

    macOS
    Chrome 142.0.0.0 美国
    2025年12月2日
    回复
  • 鲁迅黑铁

    看看我在哪儿

    macOS
    Chrome 142.0.0.0 美国
    2025年12月2日
    回复
  • 1 2 3 4 5 6 7 8 9 11 12 13 14 15 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 46 47 48 49 50 51 52 53 54 55 57 58 60 61 62 63 64 65 66 67 69 72 74 76 77 78 79 80 81 82 85 86 87 90 92 93 94 95 96 97 98 99
    取消回复

    我是人间惆怅客,知君何事泪纵横,断肠声里忆平生。

    那年今日(04月14日)

    • 2010年:中国青海玉树大地震
    • 1894年:托马斯·爱迪生展示了其新发明活动电影放映机
    • 1629年:荷兰物理学家克里斯蒂安·惠更斯出生
    • 1578年:西班牙国王腓力三世出生
    • 605年:隋炀帝下令开凿大运河
    • 更多历史事件
    最新 热点 随机
    最新 热点 随机
    Everything Claude Code 详细使用文档 配置Jackson使用字段而不是getter/setter来序列化和反序列化 这个域名注册整整十年了,十年时间,真快啊 Claude Code全维度实战指南:从入门到精通,解锁AI编程新范式 Apollo配置中心中的protalDB的作用是什么 org.apache.ibatis.plugin.Interceptor类详细介绍及使用
    AI时代,个人技术博客的出路在哪里?使用WireGuard在Ubuntu 24.04系统搭建VPN这个域名注册整整十年了,十年时间,真快啊WordPress实现用户评论等级排行榜插件WordPress网站换了个字体,差点儿把样式换崩了做了一个WordPress文章热力图插件
    开发者必懂的 AI 向量入门:从数学基础到实战应用 分代ZGC这么牛?底层原理是什么? 图解 | 原来这就是网络 使用springboot结合AI生成视频 Java枚举梳理总结一 Excel2016右键新建工作表,打开时提示“因为文件格式或文件扩展名无效。请确定文件未损坏,并且文件扩展名与文件的格式匹配。”的解决办法
    标签聚合
    设计模式 ElasticSearch docker 多线程 SpringBoot JAVA AI 分布式 MySQL JVM Spring SQL 架构 K8s IDEA WordPress 数据库 AI编程 Redis 日常
    友情链接
    • Blogs·CN
    • Honesty
    • Mr.Sun的博客
    • 临窗旋墨
    • 哥斯拉
    • 彬红茶日记
    • 志文工作室
    • 懋和道人
    • 拾趣博客导航
    • 搬砖日记
    • 旧时繁华
    • 林羽凡
    • 瓦匠个人小站
    • 皮皮社
    • 知向前端
    • 蜗牛工作室
    • 韩小韩博客
    • 风渡言

    COPYRIGHT © 2026 lifengdi.com. ALL RIGHTS RESERVED.

    域名年龄

    Theme Kratos Made By Dylan

    津ICP备2024022503号-3

    京公网安备11011502039375号