Skip to content

Detecting Emoji-Only Messages in PHP

Introduction

When building chatbots or messaging applications, you'll often need to detect when a user sends a message that contains only emojis. This is common in scenarios like:

  • Instagram story reactions - Users often reply with just "❤️" or "😂"
  • Chat reactions - Quick emoji responses like "👍" or "🔥"
  • Message filtering - Treating emoji-only messages differently from text

In this article, we'll build a simple PHP function to detect emoji-only messages using regular expressions.

The Unicode Challenge

Emojis are more complex than they appear. What looks like a single emoji can actually be multiple Unicode characters combined:

  • Basic emojis: Single code points like 😀 (U+1F600)
  • Skin tone modifiers: 👋🏽 is 👋 + 🏽 (medium skin tone)
  • ZWJ sequences: 👨‍👩‍👧 is actually 👨 + ZWJ + 👩 + ZWJ + 👧
  • Flag emojis: 🇺🇸 is made of two regional indicator symbols

This complexity means we can't just check for a single character—we need a regex pattern that covers all emoji Unicode ranges.

The Implementation

Here's a practical implementation that handles most common emojis:

php
/**
 * Checks if the text contains only emojis and whitespace.
 * Useful for detecting reactions or emoji-only messages.
 */
function isOnlyEmojis(?string $text): bool
{
    if (!$text || trim($text) === '') {
        return false;
    }

    // Remove all whitespace
    $textWithoutWhitespace = preg_replace('/\s+/u', '', $text);

    if ($textWithoutWhitespace === '') {
        return false;
    }

    // Unicode regex pattern for emojis
    $emojiPattern = '/^[\x{1F600}-\x{1F64F}' .  // Emoticons
        '\x{1F300}-\x{1F5FF}' .  // Misc Symbols and Pictographs
        '\x{1F680}-\x{1F6FF}' .  // Transport and Map
        '\x{1F700}-\x{1F77F}' .  // Alchemical Symbols
        '\x{1F780}-\x{1F7FF}' .  // Geometric Shapes Extended
        '\x{1F800}-\x{1F8FF}' .  // Supplemental Arrows-C
        '\x{1F900}-\x{1F9FF}' .  // Supplemental Symbols and Pictographs
        '\x{1FA00}-\x{1FA6F}' .  // Chess Symbols
        '\x{1FA70}-\x{1FAFF}' .  // Symbols and Pictographs Extended-A
        '\x{1F1E6}-\x{1F1FF}' .  // Flags (Regional Indicator Symbols)
        '\x{2600}-\x{26FF}' .    // Misc symbols (sun, moon, stars)
        '\x{2700}-\x{27BF}' .    // Dingbats
        '\x{2300}-\x{23FF}' .    // Misc Technical
        '\x{2B50}\x{2B55}' .     // Star and circle
        '\x{231A}\x{231B}' .     // Watch and hourglass
        '\x{2328}' .             // Keyboard
        '\x{23CF}' .             // Eject symbol
        '\x{23E9}-\x{23F3}' .    // Media control symbols
        '\x{23F8}-\x{23FA}' .    // Media control symbols
        '\x{24C2}' .             // Circled M
        '\x{25AA}\x{25AB}' .     // Small squares
        '\x{25B6}\x{25C0}' .     // Play buttons
        '\x{25FB}-\x{25FE}' .    // Squares
        '\x{2934}\x{2935}' .     // Arrows
        '\x{2B05}-\x{2B07}' .    // Arrows
        '\x{3030}' .             // Wavy dash
        '\x{303D}' .             // Part alternation mark
        '\x{3297}' .             // Circled Ideograph Congratulation
        '\x{3299}' .             // Circled Ideograph Secret
        '\x{FE0F}' .             // Variation Selector-16 (emoji presentation)
        '\x{200D}' .             // Zero Width Joiner (for combined emojis)
        ']+$/u';

    return preg_match($emojiPattern, $textWithoutWhitespace) === 1;
}

Usage Examples

php
// Basic emojis
isOnlyEmojis('😀');           // true
isOnlyEmojis('❤️🔥');         // true
isOnlyEmojis('👍👍👍');       // true

// With whitespace (still valid)
isOnlyEmojis('😀 😂 🎉');     // true

// Combined emojis (ZWJ sequences)
isOnlyEmojis('👨‍👩‍👧');         // true
isOnlyEmojis('👋🏽');          // true (skin tone modifier)

// Mixed content (not emoji-only)
isOnlyEmojis('Hello 👋');     // false
isOnlyEmojis('Nice! 🔥');     // false
isOnlyEmojis('123');          // false

// Edge cases
isOnlyEmojis('');             // false
isOnlyEmojis(null);           // false
isOnlyEmojis('   ');          // false (whitespace only)

Understanding the Unicode Ranges

Let's break down the key Unicode ranges in our pattern:

RangeDescriptionExamples
1F600-1F64FEmoticons😀 😂 😍 🙄
1F300-1F5FFMisc Symbols & Pictographs🌟 🎉 🔥 💡
1F680-1F6FFTransport & Map🚀 ✈️ 🏠
1F900-1F9FFSupplemental Symbols🤖 🦄 🧠
1F1E6-1F1FFRegional Indicators🇺🇸 🇪🇸 🇧🇷
2600-26FFMisc Symbols☀️ ⭐ ♥️
FE0FVariation SelectorMakes text symbols appear as emoji
200DZero Width JoinerConnects emojis (👨‍👩‍👧)

Why include FE0F and 200D?

The Variation Selector-16 (U+FE0F) tells the system to render a character as an emoji. The Zero Width Joiner (U+200D) connects multiple emojis into one combined emoji, like family emojis or profession emojis.

Practical Use Case: Chatbot Reactions

Here's how you might use this in a chatbot context:

php
function handleIncomingMessage(string $message): void
{
    if (isOnlyEmojis($message)) {
        // It's a reaction! Handle differently
        logReaction($message);
        // Maybe don't trigger a full AI response for just "👍"
        return;
    }

    // Process as a normal text message
    processTextMessage($message);
}

Limitations

This regex-based approach has some limitations:

  1. New emojis: Unicode adds new emojis regularly. The pattern may not cover the very latest additions.
  2. Some edge cases: Certain rarely-used emoji combinations might not be detected.
  3. Performance: For very long strings, regex can be slower than library-based approaches.

Alternative: Using a Library

For more comprehensive emoji detection, consider using a dedicated library like p3k/emoji-detector:

bash
composer require p3k/emoji-detector
php
use Emoji\Detector;

$detector = new Detector();
$result = $detector->detect('Hello 👋 World');

// Returns array of detected emojis with positions

The library approach is more robust but adds a dependency. For simple "is this emoji-only?" checks, the regex approach is lightweight and sufficient.

Conclusion

Detecting emoji-only messages is useful for many messaging applications. The key points are:

  1. Emojis are complex - They can be single characters or combinations
  2. Use Unicode ranges - Cover the main emoji blocks in your regex
  3. Don't forget ZWJ and FE0F - These invisible characters are essential for combined emojis
  4. Consider your use case - Simple regex works for most cases; use a library for advanced needs

The regex approach shown here handles the vast majority of real-world emoji usage while keeping your code dependency-free.