Main 26.750.0: точка в имени плейсхолдера больше не ломает почтовые шаблоны
Main 26.750.0 меняет три файла ядра, +16/−5 строк, и заметная правка среди них одна (вторая, в лицензионном ActivationSystem, служебная). Но сидит она в месте, где из почтового шаблона генерируется PHP-код, так что посмотреть на неё стоит всем, у кого в шаблонах есть плейсхолдеры с точкой в имени, вроде #ORDER.ID#.
Проверьте шаблоны с точкой в именах
Исправлена ветка Bitrix\Main\Mail\Internal\EventMessageTable::replaceTemplateToPhp(), которая превращает текст шаблона в PHP. Метод находит плейсхолдеры по маске /#([0-9a-zA-Z_.]+?)#/ (точка в имени разрешена), а потом подставляет найденные имена в две регулярки: в общую альтернативу, по которой ищет теги, и в паттерн для замены повторов. Имена попадали туда как есть, без экранирования. А точка в регулярке совпадает с одним любым однобайтовым символом, кроме перевода строки (модификаторов s и u у этих паттернов нет).
Тег, который встречается в шаблоне больше одного раза, метод заменяет по одному вхождению через preg_replace(…, 1). Неэкранированный паттерн /#ORDER.ID#/ цеплял и #ORDER_ID#, так что если тот стоял в тексте раньше, первая замена доставалась ему:
<?php declare(strict_types=1);
use Bitrix\Main\Mail\Internal\EventMessageTable;
echo EventMessageTable::replaceTemplateToPhp(
'Заказ #ORDER_ID#, код #ORDER.ID#, ещё раз код #ORDER.ID#'
);
до 26.750.0: Заказ <?=$arParams["ORDER.ID"];?>, код <?=$arParams["ORDER.ID"];?>, ещё раз код #ORDER.ID#
с 26.750.0: Заказ <?=$arParams["ORDER_ID"];?>, код <?=$arParams["ORDER.ID"];?>, ещё раз код <?=$arParams["ORDER.ID"];?>
До обновления на место #ORDER_ID# в письмо шло значение ORDER.ID, а последний #ORDER.ID# доходил до получателя сырым.
В общей альтернативе неэкранированная точка мешала, только если на её месте в тексте стоял символ вне маски. Рядом с #ORDER.ID# фрагмент #ORDER"ID# превращался в $arParams["ORDER"ID"], и eval сгенерированного кода падал с ParseError. Теги, которые сами проходят маску, вроде #ORDERXID#, здесь и раньше разбирались верно, потому что имя метод берёт из найденного текста.
Что поменяли в методе:
- имена плейсхолдеров при сборке альтернативы проходят через
preg_quote($k, '/'); - список имён чистится
array_unique(), и повторяющийся плейсхолдер больше не добавляет одинаковые ветки (в комментарии к коду это объяснено экономией лимита компиляции PCRE); - появилась страховка: найденный тег, который не проходит проверку
/^[0-9a-zA-Z_.]+\z/, пропускается и в PHP-литерал не попадает. В коде это названо «defence in depth», и пока работает экранирование, до этой ветки дело не дойдёт; - во второй части метода, где заменяются повторяющиеся теги, паттерн для
preg_replaceтеперь тоже строится черезpreg_quote.
Для шаблонов без точек в именах результат конвертации прежний, кроме шаблонов, где один плейсхолдер повторяется тысячи раз: там старая регулярка не компилировалась («regular expression is too large») и не срабатывала ни одна подстановка, а благодаря array_unique() теперь срабатывают. В шаблонах с именами вида #A.B# повторяющийся тег, перед которым стоит похожий тег, теперь заменяется во всех местах, #A_B# перед ним больше не получает чужое значение, а фрагмент вроде #A"B# остаётся текстом. Но всё это касается только заново собранного MESSAGE_PHP, а старый лежит в БД, пока шаблон не пересохранят.
Мелочи и находки
- Правка чинит регулярки, собранные из неэкранированных имён плейсхолдеров. Имена берутся из текста самого шаблона, а в шаблоне и так можно писать
<? ?>(метод отдельно отслеживает эти теги), иMESSAGE_PHPисполняется черезeval(EventMessageThemeCompiler.php:383, по коду в эталоне). Объяснения есть только в комментариях к коду. Новой возможности выполнить код правка не закрывает: автор шаблона и раньше мог вставить<? ?>. - Комментарии к правкам подробные и на английском, при том что остальной
EventMessageTable.phpпочти не комментирован. - Четыре «изменённые сигнатуры» в
Bitrix\Main\UpdateSystem\ActivationSystem(activateByHash,applyLicenseInfo,reincarnate,sendInfoToPartner) оказались ложной тревогой. Файл обфусцирован, его пересобрали, и у параметров новые случайные имена; типы и число аргументов те же. Логика при этом всё же поменялась: если раскодировать строки обфускатора на обоих снапшотах, вapplyLicenseInfo()видна новая ветка. Когда в ответе сервера лицензий естьBI_CONSTRUCTOR_DATE, значение пишется в опциюmain/~bi_constructor_expired_at, рядом с давней паройR7_SERVER_DATE→~UT_custom_servers_expired_at. - В
version.phpзаодно расставили пробелы после запятых вdefine. Версия собрана 27 августа (SM_VERSION_DATE). - Deprecated, новых API и изменений в БД нет.
Что делать
Публичный API не тронут. Если в почтовых шаблонах есть плейсхолдеры с точкой в имени, пересохраните эти шаблоны в админке или через EventMessageTable::update($id, ['MESSAGE' => $message]). По коду в эталоне MESSAGE_PHP хранится в БД и пересобирается только при сохранении MESSAGE (onBeforeUpdate/onBeforeAdd, EventMessageTable.php:252,270) или когда поле пустое (EventMessageCompiler.php:131–139). Без пересохранения письма так и собираются из старого MESSAGE_PHP, поэтому тестовое письмо отправляйте уже после него и сравнивайте с тем, что уходило раньше.