В процессе разработки анти-спам плагина CleanTalk для WordPress мы стокнулись с проблемой кеширования динамического JavaScript кода на фронтенде сайтов. А именно, если разместить JavaScript содержащий какие либо куски кода динамически подставляемые из бекенд сайта, то при наличии на сайте любого плагина кеширования страниц, JavaScript код становится не возможно использовать по назначению.


Рассмотрим пример


В бэкенд у нас есть шаблон JavaScript кода,


<?php
$html = '
<script type="text/javascript">
function ctSetCookie(c_name, value, def_value) {
    document.cookie = c_name + "=" + escape(value.replace(/^def_value$/, value)) + "; path=/";
}
ctSetCookie("%s", "%s", "%s");
</script>
';     

$ct_checkjs_key = rand(0,100); // Значение переменной динамическое
$field_name = 'ct_checkjs'; // Значение статическое
$ct_checkjs_def = 0; // Значение статическое

$html = sprintf($html, $field_name, $ct_checkjs_key, $ct_checkjs_def);
?>

Пример вывода на фронтенд,


<script type="text/javascript">
function ctSetCookie(c_name, value, def_value) {
document.cookie = c_name + "=" + escape(value.replace(/^def_value$/, value)) + "; path=/";
}
ctSetCookie("ct_checkjs", "455", "0");
</script>

Соответственно в кеш попадает JavaScript код в котором параметр value функции ctSetCookie неизменен на всех страницах сайта и одинаков для всех посетителей, что приводит к невозможности использования JavaScript персонально для каждого посетителя. Рассмотрим варианты решения.


Используем встроенные средства отключения кеширования


Если плагин кеширования контента на WordPress более-менее популярен, то у него обазятельно есть средства для исключения списка страниц из кеша. К примеру для WP Super cache можно указать в коде вашего плагина строчку,


define( "DONOTCACHEPAGE", true );

Этого будет достаточно чтобы страницы с вашим динамическим кодом не попадали в кеш. Минусы такого подхода,


  • Необходимо интегрировать и тестировать ваш плагин с популярными плагинами кеширования.
  • Все равно будут случаи когда ваш код некорректно отрабатывают, по причине того, что на том или ином сайте установле редко используемый плагин кеширования.
  • Ну и самое главное, такой подход сводит практически к 0 применение плагинов кеширования, если ваш JavaScript код размещается на всех страницах сайта, либо на наиболее нагруженных страницах.

Посмотрим на другой вариант решения.


AJAX вызов в бэкенд


Суть этого подхода в том, что на фронтенде размещаем только статичный JavaScript код, а все что требуется использовать динамически получаем из бекэнд сайта через AJAX вызов. Пример кода на фротенд,


//
// Делаем вызов к admin-ajax.php
//
function sendRequest(url,callback,postData) {
    var req = createXMLHTTPObject();
    if (!req) return;
    var method = "GET";
    req.open(method,url,true);
    if (postData)
            req.setRequestHeader('Content-type','application/x-www-form-urlencoded');
        req.onreadystatechange = function () {
            if (req.readyState != 4) return;
            if (req.status != 200 && req.status != 304) {
                return;
            }
        callback(req);
    };
    if (req.readyState == 4) return;
    req.send(postData);
    return null;
}
var XMLHttpFactories = [
    function () {return new XMLHttpRequest()},
    function () {return new ActiveXObject("Msxml2.XMLHTTP")},
    function () {return new ActiveXObject("Msxml3.XMLHTTP")},
    function () {return new ActiveXObject("Microsoft.XMLHTTP")}
];
function createXMLHTTPObject() {
    var xmlhttp = false;
    for (var i=0;i<XMLHttpFactories.length;i++) {
        try {
            xmlhttp = XMLHttpFactories[i]();
        }
        catch (e) {
            continue;
        }
        break;
    }
    return xmlhttp;
}

//
// Обрабатываем результаты AJAX вызова.
//
function ct_callback(req)
{
ct_cookie=req.responseText.trim();  
    ct_setCookie('ct_checkjs', ct_cookie);

return null;
}
//
// Устанавливаем куку
//
function ct_setCookie(name, value)
{
    document.cookie = name+" =; expires=Thu, 01 Jan 1970 00:00:01 GMT; path = /";
    document.cookie = name+" =; expires=Thu, 01 Jan 1970 00:00:01 GMT";

    var date = new Date;
    date.setDate(date.getDate() + 1);
    setTimeout(function() { document.cookie = name+"=" + value + "; expires=" + date.toUTCString() + "; path = /;"}, 500);

    return null;
}

var ct_ajaxurl = 'http://wordpress.local/wp-admin/admin-ajax.php';
sendRequest(ct_ajaxurl+'?'+Math.random(),ct_callback,'action=ct_get_cookie');

Прошу обратить внимание на конструкцию,


ct_ajaxurl+'?'+Math.random()

Такой прием используется для исключения кеширования в том числе и AJAX вызов.


Двигаемся к последнему листингу, посмотрим на бэкенд,


add_action( 'wp_ajax_nopriv_ct_get_cookie', 'ct_get_cookie',1 );
/**
 * Возвращает новую куку
*/
function ct_get_cookie()
{
    global $ct_checkjs_def;
    $ct_checkjs_key = ct_get_checkjs_value(true); 
    print $ct_checkjs_key;
    die();
}

Минус у такого подхода только в одном — ваш плагин делает на 1 вызов в бэкенд WordPress больше. С учетом того, что хостинг может быть не самым быстрым, либо на WordPress может быть установлено не один десяток плагинов, такой вызов приведет к увеличению времени ответа сайта.


Успехов в разработке под WordPress!

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


  1. oxidmod
    19.04.2016 14:53

    я вижу только один минус. разработка на ВП чегото отличного от бложика.
    конечно можно, но зачем есть кактус?


    1. delfi
      19.04.2016 15:08
      +1

      Как вопрос относится к данному топику?
      По описанию плагина — это антиспам и для блогов в том числе.
      WP уже давно перерос «бложики» и на нем можно делать достаточно разнообразные по тематике сайты. При том, как показывает практика, конечному администратору сайта на данной CMS, мало понимающему в верстке, программировании — очень удобно пользоваться WP.


      1. oxidmod
        19.04.2016 15:44
        +1

        объясняю как мой коммент относится к теме:
        js, который собирается на бекенде — это, мягко говоря, очень пахнущий код.

        >> Минус у такого подхода только в одном — ваш плагин делает на 1 вызов в бэкенд WordPress больше.
        зачем так мужественно побеждать ВП и его говно-код, если можно использовать чтото более подходящее?

        и да, минусить в карму — это вин, однозначно.


        1. delfi
          19.04.2016 15:59

          > js, который собирается на бекенде — это, мягко говоря, очень пахнущий код.

          Так это проблема уж не CMS, а плагинов, которые не могут предоставить возможность отключать кэш для каких-то блоков. Хотя смутно себе это представляю. Возможно, какой-то механизм должна предложить сама CMS для плагинов, чтобы не приходилось костыли писать для всевозможных плагинов кеширования.

          > зачем так мужественно побеждать ВП и его говно-код, если можно использовать чтото более подходящее?
          Кто-то побеждает, кто-то пользуется. Предложите пример хорошей CMS, на которой можно за 4-5 дня сделать хороший сайт-визитку, лендинг, за неделю-две сделать магазин, где не было бы проблем, говно-кода и т.д… При этом обычному пользователю не надо будет тратить кучу времени на панели администратора.
          И да, я за качественное исполнение в программной части, но и не стоит забывать, что конечному заказчику не важно какой код, главное, безопасный. А WP обновляется достаточно часто и еще не было у меня случаев взлома WP.

          > и да, минусить в карму — это вин, однозначно.
          Зачем это писать? Кто минусил — ему это не интересно, думаю


        1. Wedmer
          19.04.2016 16:01

          Опишите что то более подходящее. На данный момент WP хорош соответствием понятию «Из коробки».
          Большинство дргих систем требует девайс «Напильник», ssh и ftp доступ.

          Видимо от вас и ждали реплики про карму.


          1. oxidmod
            19.04.2016 16:21

            ответ вам и delfi
            лендинг, зачастую, не требует cms вовсе. это статика + просто хендлер формы для сбора контактов.
            аналогично сайт-визитка. Но любая другая ЦМС будет лучше для визитки чем wp, если нужно редактировать контент с админки.
            для магазинов есть специализированные системы, начиная с простого openCart и заканчивая такими монстрами как Magento и ShopWare (на любые потребности и карман). Даже самые простые и бесплатные e-commerce движки из коробки предоставляют все необходимое для онлайн-магазина. остается скачать с магазина тем понравившийся шаблон + пару модулей по вкусу на оплату и доставку.


            1. delfi
              19.04.2016 16:26

              Лендинг — согласен, статикой лучше.

              openCart идеален по коду? Да, есть MVC. Но как насчет костылей с qmod (или как-то так), через который написано куча плагинов, т.к. иначе их не написать, не залезая в исходники движка. Не знаю, исправили это во 2 версии или нет, но в wordpress хоть хуки есть и они покрывают 95% необходимого, когда нужно внедрить свой функционал в систему.
              И как в openCart решается вопрос, затронутый в данном топике?

              «остается скачать с магазина тем понравившийся шаблон » — в 60-70% случаев шаблон приходится допиливать под потребности клиента. Чаще клиенты заказывают отдельно дизайн. Но при чем здесь CMS?

              Вы же писали, что WP — это ужасный код. Отсюда и возник вопрос о CMS с нормальным кодом, где не нужны напильники.


              1. oxidmod
                19.04.2016 16:34

                я же написал, начиная с простого openCart.
                да, он не идеален, но всяко лучше WP.
                то что тему нужно допиливать под запрос клиента тоже не минус движка. под ВП точно также придется тему допиливать.
                если хотите прямо академический код, то смотрите на мадженто или ShopWare

                >> И как в openCart решается вопрос, затронутый в данном топике?
                не используется js генерируемый на бекенде, как и везде, кроме плагинов ВП видимо


                1. delfi
                  19.04.2016 16:44

                  > да, он не идеален, но всяко лучше WP.
                  Обоснованно, да

                  > то что тему нужно допиливать под запрос клиента тоже не минус движка. под ВП точно также придется тему допиливать.

                  Видимо, у Вас не очень большой опыт.
                  Например, делал недавно интернет магазин, где своя система скидок, для которой нет готовых плагинов, соответственно, интеграции в шаблон. Допиливать пришлось бы под любую CMS.
                  Если так рассуждать, что некоторые потребности клиента не могут быть покрыты стандартным функционалом, то тут можно начинать с корня, что значит и бизнес построен неправильно, да?

                  > если хотите прямо академический код, то смотрите на мадженто или ShopWare
                  Мадженто для магазина в 10000 товаров — это как пушкой по воробьям.

                  > не используется js генерируемый на бекенде, как и везде, кроме плагинов ВП видимо
                  Если у нас есть конфигурируемые параметры, которые затем обрабатываются с помощью JS, то как выходить из положения?
                  И да, у купленных тем тоже таких примеров куча — настройки слайдеров, например. Значит это неправильные шаблоны? Если отказываться от настраиваемости, то мы вернемся к пункту, которые обсудили ранее, где шаблон не может покрыть требования заказчика.


                  1. oxidmod
                    19.04.2016 17:06

                    >> Обоснованно, да
                    хотябы приличный MVC, который позволяет отделить мух (вывод инфы) от котлет (кастомной системы скидок)

                    >> Видимо, у Вас не очень большой опыт.
                    2.5 года в сфере ecommerce

                    >> Например, делал недавно интернет магазин, где своя система скидок, для которой нет готовых плагинов, соответственно, интеграции в шаблон. Допиливать пришлось бы под любую CMS.
                    в e-commerce движках скидки в каком либо виде присутствуют всегда. Значит в нормальном движке вам нужно подпилить/заменить БЛ (рассчет этой скидки в зависимости от нужных параметров), а уж в шаблоне они выведутся нормально

                    >> Мадженто для магазина в 10000 товаров — это как пушкой по воробьям.
                    пушка то на то и нацелена, но хорошо, вот вам другой пример: OXID eShop. В базовой поставке он запуститься на шаред хостинге с мускулем и пхп5.3. У одного из клиентов он на нем крутилось 300к товаров, но конечно дополнительно устанавливались модули для поиска на базе sphinx, для кеширования страниц на базе varnish и для кеша сущностей на базе memcahe. Тут уж конечно шаред хостингом не обойдешься, но это была все та же Community Edition движка, которая по мере роста бизнесса обростала необходимыми модулями. Сожержит удобное АПи для написания модулей любой сложности под любые запросы, хотя и не идеален с точки зрения архитектуры.

                    >> Если у нас есть конфигурируемые параметры, которые затем обрабатываются с помощью JS, то как выходить из положения?
                    уж всяко не генерацией js-а на бекенде. js должен быть статическим, и в идеале грузиться с cdn.
                    в случае слайдеров:
                    1) либо верстка слайдеров генерится на бекенде и js её оживляет, добавляя реакцию на клики/свайпы
                    2) либо js аяксом тянет с бекенда список елеметов и ихъ параметры и динамически генерит контент на странице
                    но ни в 1 ни во 2 случае ему не нужны никакие динамические параметры

                    ЗЫ: имхо, качество кода самого ВП лишь стимулирует писать под него такие же плагины.


                    1. delfi
                      19.04.2016 17:13

                      >> хотябы приличный MVC, который позволяет отделить мух (вывод инфы) от котлет (кастомной системы скидок)
                      Только, как написал выше, без внедрения в системные файлы — в openCart не внедрить свою систему. А обновили системные файлы — потеряли возможность обновляться.
                      В Wordpress реализовал это с помощью хуков, система обновляется без проблем и функционал не ломается.

                      > в e-commerce движках скидки в каком либо виде присутствуют всегда. Значит в нормальном движке вам нужно подпилить/заменить БЛ (рассчет этой скидки в зависимости от нужных параметров), а уж в шаблоне они выведутся нормально
                      Есть 3 типа товаров (как их пометить?). Для каждого типа есть свой порог для оптовой цены и для среднего опта. Скидка считается из расчета набранной суммы в корзине + по прошлым покупкам для каждого типа товара отдельно. Назовите хоть 1 плагин, который сможет такое сделать из коробки. Назовите хоть 1 шаблон, который адаптировался под все доработки такого плана.

                      >> Если у нас есть конфигурируемые параметры, которые затем обрабатываются с помощью JS, то как выходить из положения?
                      уж всяко не генерацией js-а на бекенде. js должен быть статическим, и в идеале грузиться с cdn.
                      в случае слайдеров:
                      1) либо верстка слайдеров генерится на бекенде и js её оживляет, добавляя реакцию на клики/свайпы
                      2) либо js аяксом тянет с бекенда список елеметов и ихъ параметры и динамически генерит контент на странице
                      но ни в 1 ни во 2 случае ему не нужны никакие динамические параметры

                      Хм, разве не п. 2 предлагается в статье?

                      > ЗЫ: имхо, качество кода самого ВП лишь стимулирует писать под него такие же плагины.
                      Я не спорю, что код там не очень, но иногда вопрос результата и клиентов не волнует код. CMS удобна, гибкая под разные задачи, при должном подходе не ломают


                      1. oxidmod
                        19.04.2016 17:34

                        >> Только, как написал выше, без внедрения в системные файлы — в openCart не внедрить свою систему
                        именно с модулями openCart бодаться не приходилось. хватало доступных в магазине.
                        а вот модули в magento и OXID встраиваются в систему без проблем.

                        >> Есть 3 типа товаров (как их пометить?)
                        а как вы их разделяете? по категории? по артикулу? по названиях? не факт что для этого вообще нужны какието допилы

                        >> Для каждого типа есть свой порог для оптовой цены и для среднего опта. Скидка считается из расчета набранной суммы в корзине + по прошлым покупкам для каждого типа товара отдельно. Назовите хоть 1 плагин, который сможет такое сделать из коробки. Назовите хоть 1 шаблон, который адаптировался под все доработки такого плана.
                        Скейл цены от количества искаропки:
                        http://i.piccy.info/i9/d6807c9075551da6419daf5abab0b5e6/1461076128/37491/1026532/scale.png

                        искаропки там также есть разные цены для груп юзеров, скидки для груп юзеров, смена цен по наступлению определенной даты (флеш распродажи например), расчет скидка от суммы заказа,
                        в сторе модулей есть модули бонусных систем (накопление бонусов за покупку и их последующее использование в новых заказах)

                        >> Хм, разве не п. 2 предлагается в статье?
                        в статье это предлагается как решение проблемы для ВП. хотя в нормальных системах такой пробелмы просто нет.


                        1. delfi
                          19.04.2016 17:38

                          >именно с модулями openCart бодаться не приходилось. хватало доступных в магазине.
                          50% модулей для не 2 версии устанавливаются с заменой системных файлов. Часть из них через qmod. Если просто заменяют, то 2 модуля перезатирают друг друга.
                          > а вот модули в magento и OXID встраиваются в систему без проблем.
                          Не сталкивался, не могу ничего сказать

                          >> Есть 3 типа товаров (как их пометить?)
                          а как вы их разделяете? по категории? по артикулу? по названиях? не факт что для этого вообще нужны какието допилы
                          По тегам

                          >> Для каждого типа есть свой порог для оптовой цены и для среднего опта. Скидка считается из расчета набранной суммы в корзине + по прошлым покупкам для каждого типа товара отдельно. Назовите хоть 1 плагин, который сможет такое сделать из коробки. Назовите хоть 1 шаблон, который адаптировался под все доработки такого плана.
                          Скейл цены от количества искаропки:
                          http://i.piccy.info/i9/d6807c9075551da6419daf5abab0b5e6/1461076128/37491/1026532/scale.png

                          Не подходит, считается не по количеству товара, а по общей сумме товаров определенного типа, находящегося в корзине и приобретенного ранее.

                          >искаропки там также есть разные цены для груп юзеров, скидки для груп юзеров, смена цен по наступлению определенной даты (флеш распродажи например), расчет скидка от суммы заказа,
                          в сторе модулей есть модули бонусных систем (накопление бонусов за покупку и их последующее использование в новых заказах)
                          В моем случае — было для всех групп пользователей. Бонусные начисления за покупки тоже не решают задачу.

                          >> Хм, разве не п. 2 предлагается в статье?
                          в статье это предлагается как решение проблемы для ВП. хотя в нормальных системах такой пробелмы просто нет.

                          Не совсем понимаю логики.

                          Думаю, что обсуждение ушло совсем в другое русло, поэтому, откланиваюсь. Всего хорошего


                          1. oxidmod
                            19.04.2016 17:46

                            >> Думаю, что обсуждение ушло совсем в другое русло, поэтому, откланиваюсь. Всего хорошего
                            вот не понимаю людей которые цепляются за откровенное г*но, аргументируя тем, что в других случаях всеравно нужно будет дописывать. Да любые кастомные вещи нужно дописывать. Другое дело как это дописывать и как потом поддерживать.
                            я очень плотно работал с OXID, и скажу, что модуль для рассчета скидки по сумарной стоимости продуктов сгрупированных по тегам пишеться без правок фронта от слова вообще. И при выключении этого модуля из админки ничего не сломается, просто начнет считать как считало из каробки


                            1. shagimuratov
                              19.04.2016 20:13

                              По долгу службы приходится развивать плагины для более чем 10 CMS, так вот в поддержку WordPress скажу, что тамошняя система хуков наиболее продуманная и документированная.

                              Поддерживать тамошние плагины одно удовольствие.


                              1. oxidmod
                                19.04.2016 20:30

                                открываем wp-includes/plugin.php
                                и находим:

                                function do_action($tag, $arg = '') {
                                    global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
                                    ...
                                }
                                


                                да, очень удобная и надежная система


                                1. delfi
                                  20.04.2016 12:10

                                  https://github.com/OXID-eSales/oxideshop_ce/search?utf8=%E2%9C%93&q=global&type=Code


                                  1. oxidmod
                                    20.04.2016 13:37

                                    окай, вы нашли грязный легаси код, поверх которого дана нормальная абстракция для работы. сисема постоянно развивается и со временем эти вещи будут выпилены. В то времякак в ВП это и есть основа на которой нужно работать. Но оставайтесь в своих розовых мечтах


                            1. delfi
                              20.04.2016 12:09

                              О том и речь была, что цепочку обсуждения потеряли.
                              Система скидок, которую сделал хуками не затрагивает фронт и при отключении прекрасно все работает без нее, а Вы уже все перемешали и выставили ситуацию иначе.


  1. sefus
    19.04.2016 16:10
    +2

    Чтобы js-код кэшировался, его не нужно генерировать динамически, а необходимые переменные запрашивать по ajax с сервера по динамическому адресу. Вот и вся статья.


    1. shagimuratov
      19.04.2016 20:09

      Плюс пример с готовой реализацией, так все-таки интереснее.