Pull to refresh

Случайная выборка в SSI Apache

Reading time 8 min
Views 1.1K
Часто перед разработчиком стоит задача установить определенный элемент на сайте, который случайным образом выводит информацию.Будь то коллаж в шапке, изречения философов и т.д. Задача тривиальная, но для обычных языков программирования, а если возможности использовать их нет?Java Script не в счет, зависеть от настроек пользователя в таких задачах — не кошерно. На помощь приходит SSI, хотя, положа руку на сердце, это не те задачи, которые должен выполнять.Итак, что у нас есть, и что можно сделать:
  • SSI не может получить список файлов (элементов) из папки, поэтому будем список указывать в коде не динамично;
  • В SSI нет массивов, значит будем делать псевдо массив из переменных;
  • В SSI нет математических функций, зато есть конкатенация и регулярные выражения;
  • Нет математики, но есть время, случайное число можно выбирать из секунд (кстати функция rand во многих языках программирования работает именно от времени);
  • Так как в SSI мы не можем указать диапазон случайной выборки, диапазонами может быть секунды в пределах десятка (10 элементов), в пределах минуты (60 элементов), в пределах сотни (100 элементов), и т.д.;
  • Ввиду того, что в SSI нет циклов, скрипт должен быть линейным в один проход, будет куча copy/past;
Вот такие дела. Ограничение элементов по количеству элементов — не так страшно, мы же не космический корабль делаем, а все таки костыль. Еще добавок, мы должны иметь полный список элементов, т.е. без пробелов. По ходу проявляется алгоритм:
  • Проверяем какие есть элементы, а каких нет;
  • Заполняем недостающие элементы последовательностью из существующих;
  • В SSI нет математических функций, зато есть конкатенация и регулярные выражения;
  • Выбираем соответствующий элемент относительно секунд;
Использовать будем как внешнюю компоненту, т.е. определяем список, вызываем компоненту, получаем переменную: Код (1)
    <!-- Объявляем переменные списка -->
    <!--#set var="RND_0" value="Значение 0" -->
    <!--#set var="RND_1" value="Значение 1" -->
    <!--#set var="RND_2" value="Значение 2" -->
    
    <!-- Вызываем компоненту -->
    <!--#include virtual="/rnd.shtml"-->
    
    <!-- Вот наше случайное значение -->
    [ <!--#echo var="RND_X"--> ]
    
Компоненту рекомендую делать на 10 элементов, она объемом 14 Кб, а на 60 элементов уже больше 500 Кб, вот так-то (хотя надо подумать над оптимизацией еще).Компонента: Код (2)
    <!--
        Объявляем дополнительные переменные для псевдо списков существующих
        и использованных элементов
    -->
    
    <!--#set var="RND_LIST_INDEX" value="x" -->
    <!--#set var="RND_LIST_INDEX_TMP" value="x" -->
    
    <!--
        Заполняем переменную существующих элементов и объявляем дополнительные
        переменные "флаги" существования переменных. Ввиду того, что циклов нет,
        приходится делать такой длинный копипаст.
    -->
    
    <!--#if expr="$RND_0" -->
        <!--#set var="RND_LIST_INDEX" value="${RND_LIST_INDEX}0" -->
        <!--#set var="RND_0_EX" value="1" -->
    <!--#else -->
        <!--#set var="RND_0_EX" value="0" -->
    <!--#endif -->
    <!--#if expr="$RND_1" -->
        <!--#set var="RND_LIST_INDEX" value="${RND_LIST_INDEX}1" -->
        <!--#set var="RND_1_EX" value="1" -->
    <!--#else -->
        <!--#set var="RND_1_EX" value="0" -->
    <!--#endif -->
    <!--#if expr="$RND_2" -->
        <!--#set var="RND_LIST_INDEX" value="${RND_LIST_INDEX}2" -->
        <!--#set var="RND_2_EX" value="1" -->
    <!--#else -->
        <!--#set var="RND_2_EX" value="0" -->
    <!--#endif -->
    <!--#if expr="$RND_3" -->
        <!--#set var="RND_LIST_INDEX" value="${RND_LIST_INDEX}3" -->
        <!--#set var="RND_3_EX" value="1" -->
    <!--#else -->
        <!--#set var="RND_3_EX" value="0" -->
    <!--#endif -->
    <!--#if expr="$RND_4" -->
        <!--#set var="RND_LIST_INDEX" value="${RND_LIST_INDEX}4" -->
        <!--#set var="RND_4_EX" value="1" -->
    <!--#else -->
        <!--#set var="RND_4_EX" value="0" -->
    <!--#endif -->
    <!--#if expr="$RND_5" -->
        <!--#set var="RND_LIST_INDEX" value="${RND_LIST_INDEX}5" -->
        <!--#set var="RND_5_EX" value="1" -->
    <!--#else -->
        <!--#set var="RND_5_EX" value="0" -->
    <!--#endif -->
    <!--#if expr="$RND_6" -->
        <!--#set var="RND_LIST_INDEX" value="${RND_LIST_INDEX}6" -->
        <!--#set var="RND_6_EX" value="1" -->
    <!--#else -->
        <!--#set var="RND_6_EX" value="0" -->
    <!--#endif -->
    <!--#if expr="$RND_7" -->
        <!--#set var="RND_LIST_INDEX" value="${RND_LIST_INDEX}7" -->
        <!--#set var="RND_7_EX" value="1" -->
    <!--#else -->
        <!--#set var="RND_7_EX" value="0" -->
    <!--#endif -->
    <!--#if expr="$RND_8" -->
        <!--#set var="RND_LIST_INDEX" value="${RND_LIST_INDEX}8" -->
        <!--#set var="RND_8_EX" value="1" -->
    <!--#else -->
        <!--#set var="RND_8_EX" value="0" -->
    <!--#endif -->
    <!--#if expr="$RND_9" -->
        <!--#set var="RND_LIST_INDEX" value="${RND_LIST_INDEX}9" -->
        <!--#set var="RND_9_EX" value="1" -->
    <!--#else -->
        <!--#set var="RND_9_EX" value="0" -->
    <!--#endif -->

    <!--
        Теперь по порядку устанавливаем значение переменных списка, если у переменной
        значения нет. Значение устанавливаем при условии, что мы не устанавливали
        его до этого, ввиду того, что у нас жестко определена последовательность
        от 0 до 9.
    -->
    
    <!--#if expr="$RND_0_EX = 0 && $RND_0_EX = 1 && $RND_LIST_INDEX_TMP != /0/" -->
        <!--#set var="RND_0" value="$RND_0" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}0" -->
    <!--#elif expr="$RND_0_EX = 0 && $RND_1_EX = 1 && $RND_LIST_INDEX_TMP != /1/" -->
        <!--#set var="RND_0" value="$RND_1" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}1" -->
    <!--#elif expr="$RND_0_EX = 0 && $RND_2_EX = 1 && $RND_LIST_INDEX_TMP != /2/" -->
        <!--#set var="RND_0" value="$RND_2" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}2" -->
    <!--#elif expr="$RND_0_EX = 0 && $RND_3_EX = 1 && $RND_LIST_INDEX_TMP != /3/" -->
        <!--#set var="RND_0" value="$RND_3" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}3" -->
    <!--#elif expr="$RND_0_EX = 0 && $RND_4_EX = 1 && $RND_LIST_INDEX_TMP != /4/" -->
        <!--#set var="RND_0" value="$RND_4" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}4" -->
    <!--#elif expr="$RND_0_EX = 0 && $RND_5_EX = 1 && $RND_LIST_INDEX_TMP != /5/" -->
        <!--#set var="RND_0" value="$RND_5" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}5" -->
    <!--#elif expr="$RND_0_EX = 0 && $RND_6_EX = 1 && $RND_LIST_INDEX_TMP != /6/" -->
        <!--#set var="RND_0" value="$RND_6" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}6" -->
    <!--#elif expr="$RND_0_EX = 0 && $RND_7_EX = 1 && $RND_LIST_INDEX_TMP != /7/" -->
        <!--#set var="RND_0" value="$RND_7" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}7" -->
    <!--#elif expr="$RND_0_EX = 0 && $RND_8_EX = 1 && $RND_LIST_INDEX_TMP != /8/" -->
        <!--#set var="RND_0" value="$RND_8" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}8" -->
    <!--#elif expr="$RND_0_EX = 0 && $RND_9_EX = 1 && $RND_LIST_INDEX_TMP != /9/" -->
        <!--#set var="RND_0" value="$RND_9" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}9" -->
    <!--#endif -->

    <!--
        Теперь проверяем, не заполнен ли уже список и не надо ли последовательность
        устанавливать сначала
    -->
    
    <!--#if expr="$RND_LIST_INDEX_TMP = $RND_LIST_INDEX" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="x" -->
    <!--#endif -->
    
    <!--
        Дальше точно такие же блоки для каждой их переменных по порядку для всего
        диапазона от 0 до 9. Не забываем после каждого блока проверять заполненность
        последовательности.
    -->    
    
    <!--#if expr="$RND_1_EX = 0 && $RND_0_EX = 1 && $RND_LIST_INDEX_TMP != /0/" -->
        <!--#set var="RND_1" value="$RND_0" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}0" -->
    <!--#elif expr="$RND_1_EX = 0 && $RND_1_EX = 1 && $RND_LIST_INDEX_TMP != /1/" -->
        <!--#set var="RND_1" value="$RND_1" -->
        <!--#set var="RND_LIST_INDEX_TMP" value="${RND_LIST_INDEX_TMP}1" -->
    <!--#elif expr="$RND_1_EX = 0 && $RND_2_EX = 1 && $RND_LIST_INDEX_TMP != /2/" -->
    ...
    
    <!--
        Весь список элементов заполнен, можно выбрать нужный относительно секунд.
    -->    
    
    <!--#config timefmt="%S" -->
    <!--#set var="NUM_SEC" value="$DATE_LOCAL" -->
    <!--#if expr="$NUM_SEC = /0$/" -->
        <!--#set var="RND_X" value="$RND_0" -->
    <!--#elif expr="$NUM_SEC = /1$/" -->
        <!--#set var="RND_X" value="$RND_1" -->
    <!--#elif expr="$NUM_SEC = /2$/" -->
        <!--#set var="RND_X" value="$RND_2" -->
    <!--#elif expr="$NUM_SEC = /3$/" -->
        <!--#set var="RND_X" value="$RND_3" -->
    <!--#elif expr="$NUM_SEC = /4$/" -->
        <!--#set var="RND_X" value="$RND_4" -->
    <!--#elif expr="$NUM_SEC = /5$/" -->
        <!--#set var="RND_X" value="$RND_5" -->
    <!--#elif expr="$NUM_SEC = /6$/" -->
        <!--#set var="RND_X" value="$RND_6" -->
    <!--#elif expr="$NUM_SEC = /7$/" -->
        <!--#set var="RND_X" value="$RND_7" -->
    <!--#elif expr="$NUM_SEC = /8$/" -->
        <!--#set var="RND_X" value="$RND_8" -->
    <!--#elif expr="$NUM_SEC = /9$/" -->
        <!--#set var="RND_X" value="$RND_9" -->
    <!--#else -->
        <!--#set var="RND_X" value="$RND_0" -->
    <!--#endif -->

    <!--
        ВАЖНО!!!
        Теперь желательно обнулить список, т.к. переменные все глобальные, и если
        мы будем использовать компоненту несколько раз на одной странице для разных
        списков, то может произойти смешение списков. Хотя оставим возможность не
        обнулять его.
    -->    

    <!--#if expr="$RND_DO_NOT_CLEAN_LIST"-->
        <!--#set var="RND_DO_NOT_CLEAN_LIST" value="" -->
    <!--#else -->
        <!--#set var="RND_0" value="" -->
        <!--#set var="RND_1" value="" -->
        <!--#set var="RND_2" value="" -->
        <!--#set var="RND_3" value="" -->
        <!--#set var="RND_4" value="" -->
        <!--#set var="RND_5" value="" -->
        <!--#set var="RND_6" value="" -->
        <!--#set var="RND_7" value="" -->
        <!--#set var="RND_8" value="" -->
        <!--#set var="RND_9" value="" -->
    <!--#endif -->

    <!--
        Собственно все, случайное значение находится в переменной RND_X
    -->    

    
Конечно, вероятность получения элементов сильно зависит от общего количества элементов, так для списка из трех 9 элементов мы получим последовательность
  • RND_0 => Значение 0;
  • RND_1 => Значение 1;
  • RND_2 => Значение 2;
  • RND_3 => Значение 3;
  • RND_4 => Значение 4;
  • RND_5 => Значение 5;
  • RND_6 => Значение 6;
  • RND_7 => Значение 7;
  • RND_8 => Значение 8;
  • RND_9 => Значение 0;
Как видно, вероятность показа переменной RND_0 — 1/5, а всех остальных — 1/10.Еще, после того как компонента отработает, она оставляет за собой много пустых строк, но это можно исправить путем склеивания всех строк компоненты в одну.Собственно, саму компоненту можно скачать тут.оригинал
Tags:
Hubs:
+3
Comments 7
Comments Comments 7

Articles