Обнуляющие смайлики (или почему minishop2 отправляет заказы без товаров)

Всем привет.
Буквально вчера, словил неприятную багу.

(P.S. вполне возможно что многие знают об этой баге. Но ради интереса решил воспроизвести данную багу на других сайтах — в 90% случаев ошибка воспроизводится).

Имеется сайт на MODX Revolution 2.7.3 в связке с miniShop2 2.5.0.
Бага заключалась в том, что пришёл «пустой» заказ. Т.е. все реквизиты клиента в письме и заказе есть, а вот товаров в этом заказе нет.

Долго и упорно не мог понять в чём же проблема. Благо есть вебвизор и он таки помог воспроизвести ошибку.

Дело оказалось в смайликах. Которые были вставлены в комментарий к заказу.

Как воспроизвести ошибку:
1. Кладём товар в корзину, идём в оформление заказа, заполняем поля которые надо заполнить и в комментарий к заказу вставляем любой Emoji смайлик.
Например такой: ??
1.1 Тыкаем в любое место страницы чтобы убрать фокус с поля комментария и жмём на F5.
Убеждаемся что корзина стала пустой и бага таки присутствует =)

Причина данного явления:
1. minishop2 — никак не обрабатывает поле comment. Т.е. сохраняет его «как есть».
2. Все заполненные поля заказа, minishop2 сохраняет в сессию. В том числе и комментарий.
3. MODX дублирует сессию в базу данных. Т.е. каждая сессия — это отдельная запись в базе данных (таблица: modx_session).
4. В 99% случаев, ваша база данных имеет кодировку utf8_general_ci (особо отчаянные имеют кодировку CP1251).
5. Смайлики — это какой-то там 4-х битный набор символов. Данный набор символов поддерживает кодировка utf8mb4_unicode_ci (данную кодировку разработала команда разработчиков MySQL и была специально создана для поддержки смайликов).
6. Если вы сохраните смайлик в базе данных с кодировкой utf8_general_ci, то весь текст который будет идти после смайлика — затрется.
7. Как мы уже знаем, MODX дублирует все сессии в базу. В базу записывается и сессия минишопа. Если в этой записи имеется смайлик — то такая запись 100% «грохнется» и MODX не сможет её прочитать, т.к. текст банально затрется. Если MODX не может прочитать запись — то он обнуляет сессию и создаёт новую запись.
Таким образом мы имеем обнулённую корзину.

Решение проблемы:
Самое минимальное, это нужно сделать следующее:
0. Сделать бэкап базы данных.
1. Открыть базу данных (через phpmyadmin, Navicat и т.п.) и найти таблицу: modx_session (где modx — это ваш префикс).
1.1. В данной таблице необходимо поменять кодировку у поля «data» на utf8mb4_unicode_ci
2. На сервере найти и открыть файл ../core/config/config.inc.php
2.1. И установить кодировку:
9-я строка:
$database_connection_charset = 'utf8mb4';
12-я строка:
$database_dsn = 'mysql:host=localhost;dbname=mydbname;charset=utf8mb4';
Ну а по хорошему, нужно конечно же перевести всю базу в кодировку utf8mb4_unicode_ci
Но и минимальное решение — тоже заработает.
Дмитрий Танцирев
18 июля 2020, 08:46
modx.pro
8
2 566
+8

Комментарии: 14

Василий Наумкин
18 июля 2020, 08:55
+7
На всякий случай напомню, что MODX по умолчанию использует utf8mb4 с версии 2.6.0, которая вышла в 2017 году.
    Руслан Алеев
    21 августа 2020, 10:32
    0
    А где это прописано? В процессе установки можно выбрать любую кодировку. А, к примеру, у меня config.inc.php выглядит так:
    $database_connection_charset = 'utf8';
    ...
    $database_dsn = 'mysql:host=localhost;dbname=DB_NAME;charset=utf8';
    Не пойму где именно MODX использует utf8mb4 по умолчанию, и использует ли вообще.
      Василий Наумкин
      22 августа 2020, 03:59
      0
      Ну ок, поддерживает utf8mb4 с версии 2.6.0, которая вышла в 2017 году.

      А если ты её не используешь — то сам себе злобный буратино. На modhost.pro она ставится для всех новых сайтов на MODX по умолчанию, так что я и забыл уже о подобных проблемах.
    ElenaLelo
    21 июля 2020, 23:00
    0
    Спасибо, как раз такая проблема
      Andrey Burym
      28 июля 2020, 15:45
      1
      +2
      буквально сегодня всплыла проблема ) Хотел писать заметку про вашего
      В общем, дело не только в «самайликах». Можно «проще».
      Открываем два окна/вкладки корзины, во втором оформляем заказ, доставка, оплата, адрес.
      Перед последним кликом на «Отправить» переключаемся в первую вкладку и очищаем корзину.
      Затем возвращаемся на вторую вкладку и отправляем форму. Получаем заказ без товаров.


      На minishop2.com воспроизводится, лечится… не знаю как правильно, я залечил
      case 'msOnSubmitOrder':
              $cart = $order->ms2->cart->status();
              if($cart['total_count'] < 1) {
      ....
      Согласен, что последовательность действий несколько идиотичная,
      но таких людей уже как минимум двое (я — вынужденно, за покупателя ничего не скажу ))
        Беляев Роман
        26 октября 2020, 17:45
        0
        буду третим кого это тревожит, можете на пальцах пожалуйста рассказать как запретить оформление пустых заказов, в каких файлах, какой код. Спасибо
          Дмитрий Танцирев
          26 октября 2020, 17:50
          0
          Если нужна просто проверка кол-ва товаров в корзине, то решение дали выше.
          Создаёте плагин, вешаете его на событие msOnSubmitOrder и делаете проверку.
            Беляев Роман
            26 октября 2020, 18:05
            0
            Дмитрий, спасибо.
            Но я новичок в освоении modx, и к сожалению «делаете проверку» не знаю собственно что писать.
            case 'msOnSubmitOrder':
                    $cart = $order->ms2->cart->status();
                    if($cart['total_count'] < 1) {
            ....
            это ж не весь код плагина, вот вместо точек, что должно быть?
            где создать плагин, как его подключить и повесить на собитие уже освоил, а вот сам код к сожалению не знаю
              Беляев Роман
              26 октября 2020, 18:20
              0
              <?php
              /** Проверка наличия товара в корзине */
              switch ($modx->event->name) {
              case 'msOnSubmitOrder':
              $cart = $order->ms2->cart->status();
              if($cart['total_count'] < 1) {
              Здесь что?
              }
              break;
                Andrey Burym
                26 октября 2020, 21:15
                0
                Здесь что?
                так как оформление заказа у меня несколько нестандартное (можете протестировать на kaolin-shop.ru), то для моих нужд было достаточно
                if($cart['total_count'] < 1) {
                  $modx->event->output('Ваша корзина пуста, оформление заказа невозможно');
                  $modx->event->returnedValues = 'Ваша корзина пуста, оформление заказа невозможно';
                  exit( json_encode( array('onorder' => 'error', 'message' => 'Ваша корзина пуста, оформление заказа невозможно') ) );
                }
                а так-то можете разместить любую логику.

                зы: подобную правку я сделал на единственном сайте – прочие магазины претензий не предъявляли.
                  Беляев Роман
                  26 октября 2020, 22:23
                  0
                  Супер, спасибо, сейчас попробую сделать, о результате отпишусь
                    Беляев Роман
                    27 октября 2020, 11:56
                    0
                    Статья как это делал
                    Всё получилось, прекрасно отрабатывает.
                    Написал статью пошагово как это делал, вдруг кому пригодится)))
                      Andrey Burym
                      27 октября 2020, 12:57
                      0
                      статья – это хорошо. Только exit( json_encode – мой частный случай достойный критики. И вообще-то существует $modx->toJson (если уж по-феншую))
                        Беляев Роман
                        27 октября 2020, 14:04
                        0
                        Спасибо, подправил.
                        Есть телеграмм для связи?
          Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
          14