在 PHP 中检测纯 Emoji 消息
简介
在构建聊天机器人或消息应用时,你经常需要检测用户是否发送了只包含 emoji 的消息。这在以下场景中很常见:
- Instagram 故事反应 - 用户经常只回复 "❤️" 或 "😂"
- 聊天反应 - 快速的 emoji 回复,如 "👍" 或 "🔥"
- 消息过滤 - 将纯 emoji 消息与文本消息区别处理
在本文中,我们将使用正则表达式构建一个简单的 PHP 函数来检测纯 emoji 消息。
Unicode 的挑战
Emoji 比表面看起来要复杂得多。一个看起来是单个 emoji 的字符实际上可能是多个 Unicode 字符的组合:
- 基础 emoji:单个码点,如 😀 (U+1F600)
- 肤色修饰符:👋🏽 是 👋 + 🏽(中等肤色)
- ZWJ 序列:👨👩👧 实际上是 👨 + ZWJ + 👩 + ZWJ + 👧
- 国旗 emoji:🇨🇳 由两个区域指示符号组成
这种复杂性意味着我们不能简单地检查单个字符——我们需要一个覆盖所有 emoji Unicode 范围的正则表达式模式。
实现
这是一个处理大多数常见 emoji 的实用实现:
php
/**
* 检查文本是否只包含 emoji 和空白字符。
* 用于检测反应或纯 emoji 消息。
*/
function isOnlyEmojis(?string $text): bool
{
if (!$text || trim($text) === '') {
return false;
}
// 移除所有空白字符
$textWithoutWhitespace = preg_replace('/\s+/u', '', $text);
if ($textWithoutWhitespace === '') {
return false;
}
// Emoji 的 Unicode 正则表达式模式
$emojiPattern = '/^[\x{1F600}-\x{1F64F}' . // 表情符号
'\x{1F300}-\x{1F5FF}' . // 杂项符号和象形文字
'\x{1F680}-\x{1F6FF}' . // 交通和地图
'\x{1F700}-\x{1F77F}' . // 炼金术符号
'\x{1F780}-\x{1F7FF}' . // 扩展几何形状
'\x{1F800}-\x{1F8FF}' . // 补充箭头-C
'\x{1F900}-\x{1F9FF}' . // 补充符号和象形文字
'\x{1FA00}-\x{1FA6F}' . // 国际象棋符号
'\x{1FA70}-\x{1FAFF}' . // 符号和象形文字扩展-A
'\x{1F1E6}-\x{1F1FF}' . // 国旗(区域指示符号)
'\x{2600}-\x{26FF}' . // 杂项符号(太阳、月亮、星星)
'\x{2700}-\x{27BF}' . // Dingbats
'\x{2300}-\x{23FF}' . // 杂项技术符号
'\x{2B50}\x{2B55}' . // 星星和圆圈
'\x{231A}\x{231B}' . // 手表和沙漏
'\x{2328}' . // 键盘
'\x{23CF}' . // 弹出符号
'\x{23E9}-\x{23F3}' . // 媒体控制符号
'\x{23F8}-\x{23FA}' . // 媒体控制符号
'\x{24C2}' . // 圆圈 M
'\x{25AA}\x{25AB}' . // 小方块
'\x{25B6}\x{25C0}' . // 播放按钮
'\x{25FB}-\x{25FE}' . // 方块
'\x{2934}\x{2935}' . // 箭头
'\x{2B05}-\x{2B07}' . // 箭头
'\x{3030}' . // 波浪线
'\x{303D}' . // 部分交替标记
'\x{3297}' . // 圆圈祝字
'\x{3299}' . // 圆圈秘字
'\x{FE0F}' . // 变体选择符-16(emoji 呈现)
'\x{200D}' . // 零宽连接符(用于组合 emoji)
']+$/u';
return preg_match($emojiPattern, $textWithoutWhitespace) === 1;
}使用示例
php
// 基础 emoji
isOnlyEmojis('😀'); // true
isOnlyEmojis('❤️🔥'); // true
isOnlyEmojis('👍👍👍'); // true
// 带空白字符(仍然有效)
isOnlyEmojis('😀 😂 🎉'); // true
// 组合 emoji(ZWJ 序列)
isOnlyEmojis('👨👩👧'); // true
isOnlyEmojis('👋🏽'); // true(肤色修饰符)
// 混合内容(不是纯 emoji)
isOnlyEmojis('你好 👋'); // false
isOnlyEmojis('太棒了! 🔥'); // false
isOnlyEmojis('123'); // false
// 边界情况
isOnlyEmojis(''); // false
isOnlyEmojis(null); // false
isOnlyEmojis(' '); // false(只有空白字符)理解 Unicode 范围
让我们分解模式中的主要 Unicode 范围:
| 范围 | 描述 | 示例 |
|---|---|---|
1F600-1F64F | 表情符号 | 😀 😂 😍 🙄 |
1F300-1F5FF | 杂项符号和象形文字 | 🌟 🎉 🔥 💡 |
1F680-1F6FF | 交通和地图 | 🚀 ✈️ 🏠 |
1F900-1F9FF | 补充符号 | 🤖 🦄 🧠 |
1F1E6-1F1FF | 区域指示符 | 🇺🇸 🇪🇸 🇨🇳 |
2600-26FF | 杂项符号 | ☀️ ⭐ ♥️ |
FE0F | 变体选择符 | 使文本符号显示为 emoji |
200D | 零宽连接符 | 连接 emoji(👨👩👧) |
为什么要包含 FE0F 和 200D?
变体选择符-16(U+FE0F)告诉系统将字符渲染为 emoji。零宽连接符(U+200D)将多个 emoji 连接成一个组合 emoji,如家庭 emoji 或职业 emoji。
实际用例:聊天机器人反应
以下是如何在聊天机器人上下文中使用它:
php
function handleIncomingMessage(string $message): void
{
if (isOnlyEmojis($message)) {
// 这是一个反应!区别处理
logReaction($message);
// 对于只是 "👍" 可能不需要触发完整的 AI 响应
return;
}
// 作为普通文本消息处理
processTextMessage($message);
}局限性
这种基于正则表达式的方法有一些局限性:
- 新 emoji:Unicode 定期添加新 emoji。该模式可能不包含最新的添加。
- 一些边界情况:某些很少使用的 emoji 组合可能不会被检测到。
- 性能:对于非常长的字符串,正则表达式可能比基于库的方法慢。
替代方案:使用库
对于更全面的 emoji 检测,考虑使用专用库如 p3k/emoji-detector:
bash
composer require p3k/emoji-detectorphp
use Emoji\Detector;
$detector = new Detector();
$result = $detector->detect('你好 👋 世界');
// 返回检测到的 emoji 数组及其位置库方法更健壮,但会增加一个依赖。对于简单的"这是纯 emoji 吗?"检查,正则表达式方法轻量且足够。
结论
检测纯 emoji 消息对许多消息应用很有用。关键点是:
- Emoji 很复杂 - 它们可以是单个字符或组合
- 使用 Unicode 范围 - 在正则表达式中覆盖主要的 emoji 块
- 不要忘记 ZWJ 和 FE0F - 这些不可见字符对组合 emoji 至关重要
- 考虑你的用例 - 简单的正则表达式适用于大多数情况;对于高级需求使用库
这里展示的正则表达式方法处理了绝大多数实际 emoji 使用情况,同时保持代码无依赖。

