Text\Emoji: 4-байтовые символы в колонках utf8

20.08.2026

Проблема/контекст

Колонка в кодировке utf8 (utf8mb3) в MySQL хранит максимум три байта на символ. Эмодзи и часть CJK-расширений занимают четыре байта, поэтому запись обрывается на первом таком символе и остаток текста теряется. Переводить рабочую базу в utf8mb4 ради одного поля дорого, и ядро идёт другим путём — классом Bitrix\Main\Text\Emoji.

Решение с кодом

Emoji::encode() прогоняет строку через preg_replace_callback и заменяет каждую четырёхбайтовую последовательность на ":" . bin2hex($match) . ":". Кодируются байты UTF-8, а не кодовая точка: 😊 (U+1F60A) даёт :f09f988a: — 10 символов вместо 4 байт. Паттерн покрывает плоскости 1–16 (\xF0[\x90-\xBF], [\xF1-\xF3], \xF4[\x80-\x8F]), трёхбайтовые символы не затрагиваются.

Emoji::decode() работает по собственной регулярке /:([A-F0-9]{8}):/isu и возвращает подстроку без изменений, если hex2bin() не дал валидную четырёхбайтовую последовательность. Обратная сторона: строка :f09f988a:, набранная пользователем руками, при чтении станет эмодзи.

Ключевая деталь модификаторов: getSaveModificator() и getFetchModificator() ничего не преобразуют. Каждый возвращает массив колбэков — [[Emoji::class, 'encode']] и [[Emoji::class, 'decode']]. В параметр поля кладут именно фабрику: Field::getSaveDataModifiers() вызывает её через call_user_func() и бросает SystemException, если вернулся не массив. Так объявлены поля в Bitrix\Blog\PostTable, Bitrix\Vote\QuestionTable, моделях im и landing:

        'TITLE' => [
    'data_type' => 'string',
    'save_data_modification' => [\Bitrix\Main\Text\Emoji::class, 'getSaveModificator'],
    'fetch_data_modification' => [\Bitrix\Main\Text\Emoji::class, 'getFetchModificator'],
],

    

В объектной карте полей удобнее добавлять колбэки напрямую — они проходят проверку is_callable без фабрики:

        <?php
declare(strict_types=1);

namespace Vendor\Module\Model;

use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\ORM\Fields\IntegerField;
use Bitrix\Main\ORM\Fields\StringField;
use Bitrix\Main\ORM\Fields\TextField;
use Bitrix\Main\Text\Emoji;

final class CommentTable extends DataManager
{
    public static function getTableName(): string
    {
        return 'vendor_module_comment';
    }

    public static function getMap(): array
    {
        return [
            (new IntegerField('ID'))->configurePrimary()->configureAutocomplete(),
            (new StringField('TITLE'))
                ->addSaveDataModifier([Emoji::class, 'encode'])
                ->addFetchDataModifier([Emoji::class, 'decode']),
            (new TextField('MESSAGE'))
                ->addSaveDataModifier([Emoji::class, 'encode'])
                ->addFetchDataModifier([Emoji::class, 'decode']),
        ];
    }
}

    

Save-модификаторы применяются в DataManager в add(), update() и их multi-вариантах через Field::modifyValueBeforeSave(). Fetch-модификаторы собирает Query::isFetchModificationRequired() и применяет к алиасам выборки, то есть только к полям, попавшим в select.

Границы механизма:

  • фильтр не модифицируется, поиск по значению с эмодзи требует ручного вызова:
        CommentTable::getList([
    'filter' => ['=TITLE' => Emoji::encode($userInput)],
]);

    
  • закодированное значение длиннее исходного, для varchar закладывайте запас;
  • сырые запросы через Connection модификаторы не проходят — вызывайте Emoji::encode()/decode() сами;
  • флаг колонки читается через Application::getConnection()->isUtf8mb4($table, $column) из секции utf8mb4 в connections. Компоненты соцсети кодируют текст только при false, но ORM-модификаторы срабатывают всегда, независимо от этой настройки.

Итог

Text\Emoji держит четырёхбайтовые символы в трёхбайтовой колонке ценой десяти ASCII-символов на эмодзи. Подключайте модификаторы в карте полей, а фильтры и прямые SQL-запросы кодируйте вручную.

Опубликовано 2 дня назад

Комментарии (0)

Пожалуйста, войдите в аккаунт, чтобы оставить комментарий

Оставить комментарий
Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.
AI Домовой

AI Домовой История

на связи

пишет…
Нет истории чатов
AI Домовой

Нужна авторизация

Войдите, чтобы задавать вопросы AI Домовому.

Войти