Skip to content

Categories: Selected dropdown missing and repeats categories if emojis in category names #1206

@marklchaves

Description

@marklchaves

Describe the bug

When a WordPress taxonomy term (e.g. a category) has an emoji in its name, the
Popup Maker AJAX object-search dropdown (used by conditions such as
"Categories: Name" / "Taxonomies: Name") breaks. The dropdown shows duplicate
categories (repeats) and misses some categories completely.

Video recording of the issue:
https://jam.dev/c/81bcc328-e5c0-4b6e-8ba1-a6da73a58e35

Site information

Popup Maker version: 1.22.0
WordPress version: 6.9.4
PHP version: 8.1.33

Expected behavior

The dropdown renders and searches category names that contain emoji characters
(e.g. 🎉 Sale, 📦 Products) exactly as WordPress stores them, allowing
the user to select those terms normally.

Current behavior

WordPress registers wp_staticize_emoji as an output-buffer filter for admin
AJAX responses. When the pum_object_search AJAX handler outputs its JSON
payload, WordPress intercepts the output and converts emoji Unicode characters
inside the JSON string values into <img> HTML tags, e.g.:

"text":"🎉 Sale (ID: 42)"

becomes something like:

"text":"<img src=\"...emoji/72x72/1f389.png\" ...> Sale (ID: 42)"

This corrupts the JSON structure, causing the Select2 dropdown to receive an
unparseable response or to display raw HTML markup instead of the category name.

Root cause

File: classes/Admin/Ajax.php, line 279

// Handler exits via echo + die(), not wp_send_json()
echo PUM_Utils_Array::safe_json_encode( $results );
die();

The handler builds an items array where each entry's text field contains the
raw term name:

// classes/Admin/Ajax.php:204-208  (taxonomy branch)
foreach ( $query['items'] as $id => $name ) {
    $results['items'][] = [
        'id'   => $id,
        'text' => "$name (ID: $id)",  // $name may contain emoji
    ];
}

PUM_Utils_Array::safe_json_encode() (classes/Utils/Array.php:544-546) calls
wp_json_encode() which itself is fine — but before the output reaches the
browser, WordPress's wp_staticize_emoji filter (hooked to the output buffer
for admin AJAX) rewrites emoji codepoints inside the raw JSON bytes to <img>
tags, invalidating the JSON.

The same pattern exists in the REST API fallback:
classes/RestAPI/ObjectSearch.php:264-269.

Steps to reproduce

  1. Create a WordPress category whose name includes an emoji character
    (e.g. 🎉 Promotions).
  2. Open a popup in the Popup Maker editor and navigate to the Conditions tab.
  3. Add a condition of type "Categories: Name" (or any taxonomy-name
    condition).
  4. Open the dropdown and search for the emoji category name.
  5. Observe that the dropdown either fails to load, shows blank results, or
    displays raw HTML <img> markup.

Workaround

Use the "Categories: IDs" condition variant instead of "Categories: Name".
Because IDs are integers, no emoji characters appear in the JSON response and
the issue does not occur. (This is the same pattern used to work around the
prior "Pages: Selected" encoding bug.)

Potential fix

Remove (or temporarily unhook) WordPress's emoji-staticizing filter before
outputting the JSON response, so that valid UTF-8 emoji characters are preserved
in the JSON payload:

// classes/Admin/Ajax.php — before echo on line 279
remove_filter( 'admin_footer', 'wp_print_emoji_detection_script' );

// Disable emoji conversion on the output buffer for this response.
// wp_staticize_emoji is hooked at priority 10 on output filters.
if ( function_exists( 'wp_staticize_emoji' ) ) {
    // The filter may be applied at ob level; safest is to encode
    // emoji codepoints ourselves so they survive as JSON unicode escapes.
    add_filter( 'wp_json_encode_options', function( $options ) {
        return $options | JSON_UNESCAPED_UNICODE;
    } );
}

echo PUM_Utils_Array::safe_json_encode( $results );
die();

A cleaner long-term fix is to switch from echo … die() to wp_send_json()
(which sets the correct Content-Type: application/json header) and to ensure
the emoji output-buffer filter is not running at that point, or to encode emoji
as \uXXXX sequences which survive the staticize pass intact.

The same fix should be applied to classes/RestAPI/ObjectSearch.php.

Errors

See screencast above.

Example AJAX response:

https://pumprodemo.instawp.xyz/wp-admin/admin-ajax.php?action=pum_object_search&nonce=a6f8039064&s=&paged=2&object_type=taxonomy&object_key=category&exclude=

{"items":[{"id":"81","text":"\u2665\ufe0f Social Responsibility (ID: 81)"},{"id":"82","text":"\u2696\ufe0f Economy (ID: 82)"},{"id":"80","text":"\u2699\ufe0f Development & Sustainability etc. etc.@!.etc (ID: 80)"},{"id":"74","text":"\u2708\ufe0f Travel (ID: 74)"},{"id":"75","text":"Japan (ID: 75)"},{"id":"77","text":"Tech (ID: 77)"},{"id":"69","text":"Test (ID: 69)"},{"id":"1","text":"Uncategorized (ID: 1)"},{"id":"79","text":"\ud83c\udf0f World News (ID: 79)"},{"id":"83","text":"\ud83c\udf31 Health & Wellness (ID: 83)"}],"total_count":"14"}

Total count is 14, but only 10 are returned. The list is missing the AI, Gaming, Photography, and Emoji categories.

Additional context

  • Affects all taxonomy/category name-based conditions that use the
    pum_object_search AJAX action.
  • Does not affect ID-based conditions (numeric-only values, no emoji).
  • WordPress enables emoji handling by default; disabling it site-wide via
    remove_action( 'wp_head', 'print_emoji_detection_script', 7 ) is a
    valid (but heavy-handed) site-level workaround.
  • This issue was co-written by Claude Code, so there could be errors 😬

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions