Шаблонизатор VMX::Template/Из раннего: I.GC — различия между версиями

Материал из YourcmcWiki
Перейти к: навигация, поиск
м (Новая страница: «''Данный документ оставлен в живых по исключительно историческим причинам.'' Это один из пе...»)
 
Строка 1: Строка 1:
''Данный документ оставлен в живых по исключительно историческим причинам.'' Это один из первых скриптов, которые я написал на PHP - тот самый "Index.Generate и Index.Compile", о котором говорится [[Шаблонизатор VMX::Template/Старая PHP-версия|тут]]. На этом работала моя первая-первая домашняя страничка на h11.ru. Я тогда не знал, что в базе данных бывают индексы и первичные ключи, и использовал её как INI-файл. :)
+
''Данный документ оставлен в живых по исключительно историческим причинам.'' Это один из первых скриптов, которые я написал на PHP — тот самый «Index.Generate и Index.Compile», о котором говорится [[Шаблонизатор VMX::Template/Старая PHP-версия|тут]]. На этом работала моя первая-первая домашняя страничка на h11.ru.
 +
 
 +
Ну что тут сказать?
 +
* Я тогда не знал, что в базе данных бывают индексы и первичные ключи, и использовал её как INI-файл.
 +
* Любовь к зюкам-хрюкам началась давно и сразу.
 +
* Это ж надо было столько кода понаписать…
  
 
Написано 2006-04-24. <!-- старая ссылка: http://vmx.yourcmc.ru/filelist.php?file=135 -->
 
Написано 2006-04-24. <!-- старая ссылка: http://vmx.yourcmc.ru/filelist.php?file=135 -->
Строка 5: Строка 10:
 
-----
 
-----
  
<pre>
+
== VMX index.php 1.06 ==
\----------------------/
+
  VMX index.php 1.06  
+
/----------------------\
+
</pre>
+
  
index.php - php-скрипт, который строит HTML код (на самом деле - вообще любой файл, но предназначен именно для генерации HTML-кода), основываясь на шаблоне и данных БД MySQL.
+
index.php — php-скрипт, который строит HTML код (на самом деле — вообще любой файл, но предназначен именно для генерации HTML-кода), основываясь на шаблоне и данных БД MySQL.
  
 
Зачем оно надо? Веб-дизайнер может создать ПУСТОЙ макет страницы, расставить управляющие коды и получить готовый к использованию шаблон. Далее к сайту создаётся БД (например, через gendbv2.php), и весь сайт работает сгенерированный через index.php.
 
Зачем оно надо? Веб-дизайнер может создать ПУСТОЙ макет страницы, расставить управляющие коды и получить готовый к использованию шаблон. Далее к сайту создаётся БД (например, через gendbv2.php), и весь сайт работает сгенерированный через index.php.
  
Естественно, шаблонов может быть несколько - например, один для страницы с новостями, другой для страницы с файлами, третий ещё для чего-нибудь... Несколько шаблонов вместе образуют тему. Темы располагаются в подпапке themes/ относительно папки с index.php. Каждая тема лежит в отдельной подпапке: themes/<имя_темы>
+
Естественно, шаблонов может быть несколько — например, один для страницы с новостями, другой для страницы с файлами, третий ещё для чего-нибудь… Несколько шаблонов вместе образуют тему. Темы располагаются в подпапке themes/ относительно папки с index.php. Каждая тема лежит в отдельной подпапке: themes/<имя_темы>
  
Названия всех таблиц в БД, относящихся к сайту, по умолчанию начинаются с Pages, за это отвечает переменная $PagesTable (см. config.php). Во всех таблицах должна присутствовать индексная колонка "Id". Других требований к таблицам НЕТ. При переходе в любую таблицу командой $$jmp к её имени в начало дописывается Pages (вернее, переменная $PagesTable).
+
Названия всех таблиц в БД, относящихся к сайту, по умолчанию начинаются с Pages, за это отвечает переменная $PagesTable (см. config.php). Во всех таблицах должна присутствовать индексная колонка «Id». Других требований к таблицам НЕТ. При переходе в любую таблицу командой $$jmp к её имени в начало дописывается Pages (вернее, переменная $PagesTable).
  
Конфигурационные переменные $db, $dbhost, $dbuser, $dbpwd - отвечают за имя БД, хост, на котором расположен сервер MySQL, имя пользователя MySQL и пароль MySQL соответственно.
+
Конфигурационные переменные $db, $dbhost, $dbuser, $dbpwd — отвечают за имя БД, хост, на котором расположен сервер MySQL, имя пользователя MySQL и пароль MySQL соответственно.
  
index.php следует вызывать так: "<tt><nowiki>index.php?id=<ИНДЕКС_СТРАНИЦЫ_В_ТАБЛИЦЕ_СТРАНИЦ_БД>&t=<ШАБЛОН>&theme=<ТЕМА></nowiki></tt>". Ни один из этих трёх параметров не является обязательным. По умолчанию id=1, theme=default, t=default, т.е шаблон по умолчанию - это "templates/default/default".
+
index.php следует вызывать так: «<tt><nowiki>index.php?id=<ИНДЕКС_СТРАНИЦЫ_В_ТАБЛИЦЕ_СТРАНИЦ_БД>&t=<ШАБЛОН>&theme=<ТЕМА></nowiki></tt>». Ни один из этих трёх параметров не является обязательным. По умолчанию id=1, theme=default, t=default, т.е шаблон по умолчанию — это «templates/default/default».
  
Шаблон - любой текстовый файл, в который добавлены управляющие коды.
+
Шаблон — любой текстовый файл, в который добавлены управляющие коды.
  
В самом начале обрабатываются все команды "$$include$FILE" - см.ниже.
+
В самом начале обрабатываются все команды «$$include$FILE» — см.ниже.
  
 
Потом обрабатываются подстановки типа:
 
Потом обрабатываются подстановки типа:
;<tt><nowiki><<i>></nowiki></tt> (i - число): подставит значение Id'а записи таблицы, обрабатываемой на уровне вложенности i (0 - весь файл, 1,2,... - циклы)
+
;<tt><nowiki><<i>></nowiki></tt> (i — число): подставит значение Id’а записи таблицы, обрабатываемой на уровне вложенности i (0 — весь файл, 1,2,… — циклы)
 
;<tt><<#>></tt>: подставит значение текущей обрабатываемой записи в таблице.
 
;<tt><<#>></tt>: подставит значение текущей обрабатываемой записи в таблице.
 
;<tt><<#STR>></tt>: выводится номер записи, обрабатываемой на текущем+длина(STR) уровне вложенности
 
;<tt><<#STR>></tt>: выводится номер записи, обрабатываемой на текущем+длина(STR) уровне вложенности
 
;<tt><<#-STR>></tt>: выводится номер записи, обрабатываемой на текущем-1-длина(STR) уровне вложенности
 
;<tt><<#-STR>></tt>: выводится номер записи, обрабатываемой на текущем-1-длина(STR) уровне вложенности
;<tt><<##STR>></tt>: выведется <<#STR>>
+
;<tt><<##STR>></tt>: выведется «#STR»
 
;<tt><<%>></tt>: номер текущей итерации
 
;<tt><<%>></tt>: номер текущей итерации
 
;<tt><<@>></tt>: индекс последней вставленной в БД записи (используется функция mysql_insert_id())
 
;<tt><<@>></tt>: индекс последней вставленной в БД записи (используется функция mysql_insert_id())
  
Имя переменной - далее VAR - это одно из:
+
Имя переменной — далее VAR — это одно из:
  
 
;просто строка: тогда это не переменная
 
;просто строка: тогда это не переменная
Строка 43: Строка 44:
 
:;<tt><nowiki>*{Inum}</nowiki></tt>: номер текущей итерации цикла, начиная с нуля
 
:;<tt><nowiki>*{Inum}</nowiki></tt>: номер текущей итерации цикла, начиная с нуля
 
:;*<tt><nowiki>{amy_query_count}</nowiki></tt>: количество произведённых SQL запросов
 
:;*<tt><nowiki>{amy_query_count}</nowiki></tt>: количество произведённых SQL запросов
;<tt><nowiki>[FieldName]</nowiki></tt>: тогда значение будет прочитано из поля FieldName текущей обрабатываемой записи. Эквивалентно [<<#>>\FieldName], где # - номер текущего уровня вложенности.
+
;<tt><nowiki>[FieldName]</nowiki></tt>: тогда значение будет прочитано из поля FieldName текущей обрабатываемой записи. Эквивалентно [<<#>>\FieldName], где # — номер текущего уровня вложенности.
 
;<tt><nowiki>[Id\FieldName]</nowiki></tt>: тогда значение будет прочитано из поля FieldName записи с индексом Id текущей таблицы.
 
;<tt><nowiki>[Id\FieldName]</nowiki></tt>: тогда значение будет прочитано из поля FieldName записи с индексом Id текущей таблицы.
;<tt><nowiki>[#Special\FieldName]</nowiki></tt>: специальные значения. Вызывают SQL-запрос вида "<tt><nowiki>SELECT Special(FieldName) FROM `TableName`</nowiki></tt>".
+
;<tt><nowiki>[#Special\FieldName]</nowiki></tt>: специальные значения. Вызывают SQL-запрос вида «<tt><nowiki>SELECT Special(FieldName) FROM `TableName`</nowiki></tt>».
  
 
Примеры:
 
Примеры:
Строка 52: Строка 53:
 
;<tt>[#MAX\Id]</tt>: максимальное значение поля Id в текущей таблице
 
;<tt>[#MAX\Id]</tt>: максимальное значение поля Id в текущей таблице
  
Если перед любым из этих выражений поставить \, то она преобразуется через mysql_escape_string. Если @, то как html_pbr (преобразование переводов строк в <tt><nowiki><br /></nowiki></tt>, а символов <tt>&<>"'</tt> - в HTML-entity). Если & - тогда преобразование заключается в удалении всех HTML тэгов кроме разрешённых в config.php. Если поставить \\, то это воспримется просто как символ \. Если @@ - то @. Если && - просто &.
+
Если перед любым из этих выражений поставить \, то она преобразуется через mysql_escape_string. Если @, то как html_pbr (преобразование переводов строк в <tt><nowiki><br /></nowiki></tt>, а символов <tt>&<>"'</tt> — в HTML-entity). Если & — тогда преобразование заключается в удалении всех HTML тэгов кроме разрешённых в config.php. Если поставить \\, то это воспримется просто как символ \. Если @@ — то @. Если && — просто &.
  
Итак: вне команд $VAR$ - подстановка переменной. В командах - смотрите отдельно :)
+
Итак: вне команд $VAR$ — подстановка переменной. В командах — смотрите отдельно :)
  
 
Команды (ставятся на отдельную строку):
 
Команды (ставятся на отдельную строку):
  
;$$sql$QUERY: выполнить запрос QUERY. QUERY - может содержать подстановки переменных.
+
;<tt>$$sql$QUERY</tt>: выполнить запрос QUERY. QUERY — может содержать подстановки переменных.
;$$qor$QUERY: выполнить запрос QUERY и обработать результат в цикле.
+
;<tt>$$qor$QUERY</tt>: выполнить запрос QUERY и обработать результат в цикле.
;$$wor$table$where: частный случай предыдущего:
+
;<tt>$$wor$table$where</tt>: частный случай предыдущего:
:<tt>SELECT * FROM `table` WHERE where</tt>
+
: <tt>SELECT * FROM `table` WHERE where</tt>
:Значит - обработать в цикле все записи table, соответствующие WHERE-выражению where. where - это вся часть строки после $$wor$. Примечание: Если не хотите никуда переходить - напишите $$wor${table}$... Т.к глобальная переменная PHP table - это текущая таблица.
+
: Значит — обработать в цикле все записи table, соответствующие WHERE-выражению where. where — это вся часть строки после $$wor$. Примечание: Если не хотите никуда переходить — напишите $$wor${table}$Т.к глобальная переменная PHP table — это текущая таблица.
:Примеры:
+
: Примеры:
:;$$wor$1: обойти всю таблицу.
+
:;<tt>$$wor$1</tt>: обойти всю таблицу.
:;$$wor$Id>=1 AND Id<=5: обойти записи с индексами от 1 до 5.
+
:;<tt>$$wor$Id>=1 AND Id<=5</tt>: обойти записи с индексами от 1 до 5.
:;$$wor$Id>=1 LIMIT 5: обход 5 записей, начиная с индексов больших 1. (см. 9 подсказку)
+
:;<tt>$$wor$Id>=1 LIMIT 5</tt>: обход 5 записей, начиная с индексов больших 1. (см. 9 подсказку)
;$$for$table$VAR1$VAR2: Частный случай цикла wor. Обход Id>=VAR1 AND Id<VAR1+VAR2
+
;<tt>$$for$table$VAR1$VAR2</tt>: Частный случай цикла wor. Обход Id>=VAR1 AND Id<VAR1+VAR2
;$$filter$VAR1$VAR2: Цикл, выбирающий из текущего кэша записи, у которых значение колонки VAR1 равно VAR2
+
;<tt>$$filter$VAR1$VAR2</tt>: Цикл, выбирающий из текущего кэша записи, у которых значение колонки VAR1 равно VAR2
;$$whileXX$VAR1$VAR2: Цикл повторяющий своё тело до тех пор, пока VAR1 XX VAR2 истинно
+
;<tt>$$whileXX$VAR1$VAR2</tt>: Цикл повторяющий своё тело до тех пор, пока VAR1 XX VAR2 истинно
;$$cache$WHERE: Кэширование текущей записи или всех записей, соответствующих WHERE
+
;<tt>$$cache$WHERE</tt>: Кэширование текущей записи или всех записей, соответствующих WHERE
;$$cache: Кэширование текущей записи
+
;<tt>$$cache</tt>: Кэширование текущей записи
;$$cacheassign$LEVEL: присвоить текущий кэш равным кэшу уровня LEVEL  
+
;<tt>$$cacheassign$LEVEL</tt>: присвоить текущий кэш равным кэшу уровня LEVEL  
;$$cleancache: Очистка кэша
+
;<tt>$$cleancache</tt>: Очистка кэша
;$$exit: Останов или выход из итерации цикла
+
;<tt>$$exit</tt>: Останов или выход из итерации цикла
;$$break: Выход из цикла
+
;<tt>$$break</tt>: Выход из цикла
;$$block: Начало блока (например, if'a)
+
;<tt>$$block</tt>: Начало блока (например, if'a)
 
:Пример:
 
:Пример:
:$$block
+
:<tt>$$block</tt>
:$$if<?$VAR1$VAR2
+
:<tt>$$if<?$VAR1$VAR2</tt>
:...
+
:<tt>...</tt>
:$$/
+
:<tt>$$/</tt>
 
:- это if (VAR1 < VAR2) { ... }
 
:- это if (VAR1 < VAR2) { ... }
;$$/: конец блока или тела цикла
+
;<tt>$$/</tt>: конец блока или тела цикла
;$$ifXX$VAR1$VAR2: оператор условного выхода - проверить VAR1 XX VAR2. Если ложь, выйти. Для цикла "выйти" значит "перейти к следующей записи таблицы". Внимание! Не используйте это в циклах - будет неоптимально.
+
;<tt>$$ifXX$VAR1$VAR2</tt>: оператор условного выхода - проверить VAR1 XX VAR2. Если ложь, выйти. Для цикла "выйти" значит "перейти к следующей записи таблицы". Внимание! Не используйте это в циклах - будет неоптимально.
;$$include$FILE: прямое включение шаблона. В FILE никаких переменных! Кроме того, эта команда ДОЛЖНА стоять на отдельной строке.
+
;<tt>$$include$FILE</tt>: прямое включение шаблона. В FILE никаких переменных! Кроме того, эта команда ДОЛЖНА стоять на отдельной строке.
;$$php$VAR: включение и выполнение php-кода из файла VAR.
+
;<tt>$$php$VAR</tt>: включение и выполнение php-кода из файла VAR.
;$$chg$VAR: переключение на обработку записи под индексом VAR. ВНИМАНИЕ! О кэшировании новой записи она не заботится.
+
;<tt>$$chg$VAR</tt>: переключение на обработку записи под индексом VAR. ВНИМАНИЕ! О кэшировании новой записи она не заботится.
;$$wtime: вывести текущее время выполнения.
+
;<tt>$$wtime</tt>: вывести текущее время выполнения.
  
 
Вниманию оптимизаторов! Следующие три команды очищают кэш.
 
Вниманию оптимизаторов! Следующие три команды очищают кэш.
  
;$$jmp$VAR: перейти в таблицу с именем VAR.
+
;<tt>$$jmp$VAR</tt>: перейти в таблицу с именем VAR.
;$$ret: вернуться на предыдущую таблицу (история хранится)
+
;<tt>$$ret</tt>: вернуться на предыдущую таблицу (история хранится)
;$$aret: вернуться на главную таблицу и очистить историю переходов
+
;<tt>$$aret</tt>: вернуться на главную таблицу и очистить историю переходов
 
Поддерживается вложенная буферизация вывода в 3 режимах - добавление в буфер справа (A+B = AB), слева (A+B = BA), или без буферизации. По умолчанию буферизация 1 уровня справа.
 
Поддерживается вложенная буферизация вывода в 3 режимах - добавление в буфер справа (A+B = AB), слева (A+B = BA), или без буферизации. По умолчанию буферизация 1 уровня справа.
;$$bufstartR: начать новый уровень буферизации справа
 
;$$bufstartL: начать новый уровень буферизации слева
 
;$$bufchgR: изменить метод буферизации на буферизацию справа
 
;$$bufchgL: изменить метод буферизации на буферизацию слева
 
;$$bufclear: очистить буфер текущего уровня наффиК
 
;$$bufflush: сбросить буфер текущего уровня в буфер предыдущего уровня
 
;$$bufstop: закончить текущий уровень буферизации и сбросить буфер в предыдущий
 
;$$bufsave$name: сохранить буфер под именем name (обращаться потом как ${{name}}$)
 
;$$save$name$value: сохранить value как name; для index.php $$define$name$value делает то же самое.
 
  
compile.php - специфичные команды и подстановки:
+
;<tt>$$bufstartR</tt>: начать новый уровень буферизации справа
 +
;<tt>$$bufstartL</tt>: начать новый уровень буферизации слева
 +
;<tt>$$bufchgR</tt>: изменить метод буферизации на буферизацию справа
 +
;<tt>$$bufchgL</tt>: изменить метод буферизации на буферизацию слева
 +
;<tt>$$bufclear</tt>: очистить буфер текущего уровня наффиК
 +
;<tt>$$bufflush</tt>: сбросить буфер текущего уровня в буфер предыдущего уровня
 +
;<tt>$$bufstop</tt>: закончить текущий уровень буферизации и сбросить буфер в предыдущий
 +
;<tt>$$bufsave$name</tt>: сохранить буфер под именем name (обращаться потом как ${{name}}$)
 +
;<tt>$$save$name$value</tt>: сохранить value как name; для index.php $$define$name$value делает то же самое.
 +
 
 +
<tt>compile.php</tt> - специфичные команды и подстановки:
  
;$$define$name$value: сохранить value как name НА ВРЕМЯ КОМПИЛЯЦИИ. То есть, разница между $$save и $$define при компиляции такая: если написать $$save$name$value - в php-код запишется строка типа $saved_buffers [name] = value; а при использовании define значение будет подставляться прямо в то место, где используется. При этом все подстановки внутри него всё равно работают.
+
;<tt>$$define$name$value</tt>: сохранить value как name НА ВРЕМЯ КОМПИЛЯЦИИ. То есть, разница между $$save и $$define при компиляции такая: если написать $$save$name$value - в php-код запишется строка типа $saved_buffers [name] = value; а при использовании define значение будет подставляться прямо в то место, где используется. При этом все подстановки внутри него всё равно работают.
;$$phpverbatim$filename.php: прямое включение в выходной php-код отрезка файла filename.php начиная с первого <?php и кончая первым ?>
+
;<tt>$$phpverbatim$filename.php</tt>: прямое включение в выходной php-код отрезка файла filename.php начиная с первого <?php и кончая первым ?>
;$$phpfunc$filename.php: создастся функция с телом, равным отрезку файла filename.php начиная с первого <?php и кончая первым ?>, и тут же и вызовется
+
;<tt>$$phpfunc$filename.php</tt>: создастся функция с телом, равным отрезку файла filename.php начиная с первого <?php и кончая первым ?>, и тут же и вызовется
;подстановка {(VarName)}: это как раз подстановка define'ов.
+
;<tt>подстановка {(VarName)}</tt>: это как раз подстановка define’ов.
  
 
Логические операции (XX в $$if и $$while):
 
Логические операции (XX в $$if и $$while):
Строка 119: Строка 121:
 
;==: численное/лексикографическое сравнение на "равно".
 
;==: численное/лексикографическое сравнение на "равно".
 
;!=: численное/лексикографическое сравнение на "не равно".
 
;!=: численное/лексикографическое сравнение на "не равно".
;>=: численное/лексикографическое сравнение на "больше или равно".
+
;>=: численное/лексикографическое сравнение на «больше или равно».
;>: численное/лексикографическое сравнение на "больше".
+
;>: численное/лексикографическое сравнение на «больше».
;?: проверка, задана ли глобальная переменная PHP VAR1 - true если задана и непуста как строка
+
;?: проверка, задана ли глобальная переменная PHP VAR1 — true если задана и непуста как строка
;!?: отрицание "?"
+
;!?: отрицание «?»
  
 
Причём, в именах полей/идентификаторах также можно использовать <tt><nowiki><<i>></nowiki></tt> т.к всегда и сначала подставляются <tt><nowiki><<i>></nowiki></tt>.
 
Причём, в именах полей/идентификаторах также можно использовать <tt><nowiki><<i>></nowiki></tt> т.к всегда и сначала подставляются <tt><nowiki><<i>></nowiki></tt>.
  
Для оптимизации работы с БД записи кэшируются. Вначале, при запуске скрипта кэшируется не больше одной записи с Id равным $recid. Однако потом, в циклах, кэшируется сразу много записей. А именно - все, подходящие под границы цикла. В принципе, это может создать проблемы с памятью при обработке действительно крупных объёмов данных. Но на практике всё будет нормально.
+
Для оптимизации работы с БД записи кэшируются. Вначале, при запуске скрипта кэшируется не больше одной записи с Id равным $recid. Однако потом, в циклах, кэшируется сразу много записей. А именно — все, подходящие под границы цикла. В принципе, это может создать проблемы с памятью при обработке действительно крупных объёмов данных. Но на практике всё будет нормально.
  
Далее, compile.php. Как и видно из названия - этот скрипт компилирует шаблоны. То есть, создаёт PHP скрипты, делающие то же самое, что и index.php при интерпретации шаблонов. Вызывать его следует так же, как и index.php: <tt><nowiki>/compile.php[?t=TEMPLATE][&theme=THEME]</nowiki></tt>. При этом выход запишется в файл THEME_TEMPLATE.php в ту же папку, где лежит сам compile.php. Шаблоны он берёт оттуда же, откуда и index.php. При работе compile.php меняет все вхождения <tt><nowiki>/index.php[?t=...][&theme=...]</nowiki></tt> на вызов соответствующего откомпилированного скрипта.
+
Далее, compile.php. Как и видно из названия — этот скрипт компилирует шаблоны. То есть, создаёт PHP скрипты, делающие то же самое, что и index.php при интерпретации шаблонов. Вызывать его следует так же, как и index.php: <tt><nowiki>/compile.php[?t=TEMPLATE][&theme=THEME]</nowiki></tt>. При этом выход запишется в файл THEME_TEMPLATE.php в ту же папку, где лежит сам compile.php. Шаблоны он берёт оттуда же, откуда и index.php. При работе compile.php меняет все вхождения <tt><nowiki>/index.php[?t=...][&theme=...]</nowiki></tt> на вызов соответствующего откомпилированного скрипта.
  
 
Кроме того, вы можете указать дополнительные опции компилятора шаблонов:
 
Кроме того, вы можете указать дополнительные опции компилятора шаблонов:
;&no_cache_when_reading: не обновлять кэш, если в нём нету переменной - немного оптимизирует скрипт по времени работы. Не обновлять - то есть не записывать код, производящий обновление кэша.
+
;&no_cache_when_reading: не обновлять кэш, если в нём нету переменной — немного оптимизирует скрипт по времени работы. Не обновлять — то есть не записывать код, производящий обновление кэша.
;&generate_comments: генерировать комментарии - записывать каждый кусок файла в комментарии перед командами, ему соответствующими.
+
;&generate_comments: генерировать комментарии — записывать каждый кусок файла в комментарии перед командами, ему соответствующими.
;&default_theme=DEF: переименовать тему по умолчанию в DEF, если DEF пусто - тогда имена файлов будут просто TNAME.php.
+
;&default_theme=DEF: переименовать тему по умолчанию в DEF, если DEF пусто — тогда имена файлов будут просто TNAME.php.
 
;&default_t=DEF: переименовать шаблон по умолчанию в DEF.
 
;&default_t=DEF: переименовать шаблон по умолчанию в DEF.
:Переименование влияет на имена подставляемых вместо /index.php[?t=...][&theme=...] скриптов и на имя выходного скрипта.
+
: Переименование влияет на имена подставляемых вместо /index.php[?t=][&theme=] скриптов и на имя выходного скрипта.
  
Также для удобства создан скрипт compileui.php - пользовательский интерфейс компилятора. Заполните поля ввода и нажмите кнопку "компилировать тему" - после этого все шаблоны темы с названием, заданным в поле "тема", кроме шаблонов, лежащих в файлах с расширениями, откомпилируются и будут помещены в ту же папку, где лежит compileui.php. compile.php для работы через compileui.php должен находиться в корневой папке сервера.
+
Также для удобства создан скрипт compileui.php — пользовательский интерфейс компилятора. Заполните поля ввода и нажмите кнопку «компилировать тему» — после этого все шаблоны темы с названием, заданным в поле «тема», кроме шаблонов, лежащих в файлах с расширениями, откомпилируются и будут помещены в ту же папку, где лежит compileui.php. compile.php для работы через compileui.php должен находиться в корневой папке сервера.
  
* Поле "тема" задаёт компилируемую тему.
+
* Поле «тема» задаёт компилируемую тему.
* Поле "тема default=" - аналог параметра компилятора &default_theme=DEF.
+
* Поле «тема default=" - аналог параметра компилятора &default_theme=DEF.
 
* Поле "шаблон default=" - аналог параметра компилятора &default_t=DEF.
 
* Поле "шаблон default=" - аналог параметра компилятора &default_t=DEF.
* Флажок "Управляю кэшем вручную и корректно" - аналог опции компилятора &no_cache_when_reading.
+
* Флажок "Управляю кэшем вручную и корректно» — аналог опции компилятора &no_cache_when_reading.
* Флажок "Генерировать комментарии к коду" - аналог опции компилятора &generate_comments.
+
* Флажок «Генерировать комментарии к коду» — аналог опции компилятора &generate_comments.
  
<pre>
+
== Подсказки разработчикам ==
\-------------------------/
+
  Подсказки разработчикам
+
/-------------------------\
+
</pre>
+
  
# Во-первых, в принципе, скрипт не ориентирован на ввод информации в БД, хотя он вполне может осуществляться - через команду $$sql, формы и глобальные переменные PHP. Все переменные запроса импортируются с req_ перед названием. То есть, если у вас на странице есть что-то вроде <tt><nowiki><input type="text" name="helloworld" maxlength=256></nowiki></tt> - то после отправки формы текст из этого поля ввода появится как переменная ${req_helloworld}$. Соответственно, вы можете использовать её в SQL запросе.
+
# Во-первых, в принципе, скрипт не ориентирован на ввод информации в БД, хотя он вполне может осуществляться — через команду $$sql, формы и глобальные переменные PHP. Все переменные запроса импортируются с req_ перед названием. То есть, если у вас на странице есть что-то вроде <tt><nowiki><input type="text" name="helloworld" maxlength=256></nowiki></tt> — то после отправки формы текст из этого поля ввода появится как переменная ${req_helloworld}$. Соответственно, вы можете использовать её в SQL запросе.
# В ту же тему: если ваш движок должен поддерживать некую конфигурацию через config.php - допишите в него между "<?php" и "?>" строки вида "$YOUR_VAR_NAME = YOUR_VAR_VALUE;" и далее сможете использовать их как ${YOUR_VAR_NAME}$.
+
# В ту же тему: если ваш движок должен поддерживать некую конфигурацию через config.php — допишите в него между «<?php" и "?>» строки вида «$YOUR_VAR_NAME = YOUR_VAR_VALUE;» и далее сможете использовать их как ${YOUR_VAR_NAME}$.
# Чтобы включить форму аутентификации - нужно написать ОТДЕЛЬНО два php-скрипта - один для вывода формы или приветствия, а другой для обработки команд аутетификации. Связано это с тем, что протокол HTTP не позволяет передавать HTTP Cookies иначе, чем как ДО НАЧАЛА любого вывода скрипта. То есть - скрипт-обработчик команд "вход" и "выход" должен быть включён в САМОМ начале шаблона - на первой строке. Так же и с любыми скриптами, работающими с HTTP Cookies. Скрипт, выводящий форму или приветствие, вы можете размещать где угодно. '''UPD: ЛИБО (проще) в начале включаете буферизацию справа ($$buf+r), и выводите страницу.'''
+
# Чтобы включить форму аутентификации — нужно написать ОТДЕЛЬНО два php-скрипта — один для вывода формы или приветствия, а другой для обработки команд аутетификации. Связано это с тем, что протокол HTTP не позволяет передавать HTTP Cookies иначе, чем как ДО НАЧАЛА любого вывода скрипта. То есть — скрипт-обработчик команд «вход» и «выход» должен быть включён в САМОМ начале шаблона — на первой строке. Так же и с любыми скриптами, работающими с HTTP Cookies. Скрипт, выводящий форму или приветствие, вы можете размещать где угодно. '''UPD: ЛИБО (проще) в начале включаете буферизацию справа ($$buf+r), и выводите страницу.'''
# Старайтесь оптимизировать работу с базой данных: именно MySQL является узким местом в производительности. В принципе, для этого много умения не надо :) но, например: допустим, есть таблица с категориями файлов, которые образуют иерархическую структуру. Категорий файлов, как правило, немного, и их можно кэшировать прямо все, потому что иерархический обход с использованием while - штука, которая без ручного кэширования кушать запросы будет только так. Соответственно, перед while переходим в требуемую таблицу, кэшируем все её записи - $$cache$1, и дальше начинаем while.
+
# Старайтесь оптимизировать работу с базой данных: именно MySQL является узким местом в производительности. В принципе, для этого много умения не надо :) но, например: допустим, есть таблица с категориями файлов, которые образуют иерархическую структуру. Категорий файлов, как правило, немного, и их можно кэшировать прямо все, потому что иерархический обход с использованием while — штука, которая без ручного кэширования кушать запросы будет только так. Соответственно, перед while переходим в требуемую таблицу, кэшируем все её записи — $$cache$1, и дальше начинаем while.
# Цикл идентифицирует итерации по колонке Id. Так что если её в строках результата не будет, она, хы, доопределится :) причём - отрицательными значениями. Чтобы было ясно, что это 'несуществующие' индексы.
+
# Цикл идентифицирует итерации по колонке Id. Так что если её в строках результата не будет, она, хы, доопределится :) причём — отрицательными значениями. Чтобы было ясно, что это 'несуществующие' индексы.
# Т.к $$sql требуется обычно для SQL-выбора из нескольких таблиц, то имейте ввиду, что в теле цикла, мягко говоря, не рекомендуется использовать записи с индексами кроме выбранных самим запросом - если выбор шёл из одной таблицы они, при отсутствии в кэше, подгрузятся, а здесь "угадать" таблицу скрипт не сможет.
+
# Т.к $$sql требуется обычно для SQL-выбора из нескольких таблиц, то имейте ввиду, что в теле цикла, мягко говоря, не рекомендуется использовать записи с индексами кроме выбранных самим запросом — если выбор шёл из одной таблицы они, при отсутствии в кэше, подгрузятся, а здесь «угадать» таблицу скрипт не сможет.
# Не используйте в циклах операторы условного выхода - неоптимально.
+
# Не используйте в циклах операторы условного выхода — неоптимально.
# При включении PHP-скрипта командой $$php скрипт будет "по умолчанию" видеть следующие 3 глобальные переменные:
+
# При включении PHP-скрипта командой $$php скрипт будет «по умолчанию» видеть следующие 3 глобальные переменные:
#* $link - MySQL соединение с БД;
+
#* $link — MySQL соединение с БД;
#* $result - MySQL результат запроса;
+
#* $result — MySQL результат запроса;
#* $table - имя текущей таблица;
+
#* $table — имя текущей таблица;
 
#: Осторожно обращайтесь с этими переменными во включаемых php-скриптах. При их повреждении страница, скорее всего, отобразится некорректно.
 
#: Осторожно обращайтесь с этими переменными во включаемых php-скриптах. При их повреждении страница, скорее всего, отобразится некорректно.
# "$$wor$Id>=1 AND Id<=5" и "$$wor$Id>=1 LIMIT 5" - это разные вещи. Потому что если, например, индексов >=1 и <=5 нету, а начинаются они, например, лишь с 7, то первая команда не обойдёт ничего, а вторая обойдёт <= 5 записей, начиная с 7-ой.
+
# «$$wor$Id>=1 AND Id<=5" и "$$wor$Id>=1 LIMIT 5» — это разные вещи. Потому что если, например, индексов >=1 и <=5 нету, а начинаются они, например, лишь с 7, то первая команда не обойдёт ничего, а вторая обойдёт <= 5 записей, начиная с 7-ой.
 
# Вообще говоря - $$include$FILE - это не команда. Собственно интерпретатор даже не видит такие команды - они обрабатываются до него и прозрачно.
 
# Вообще говоря - $$include$FILE - это не команда. Собственно интерпретатор даже не видит такие команды - они обрабатываются до него и прозрачно.
  
<pre>
+
== Примеры ==
\---------/
+
  Примеры
+
/---------\
+
</pre>
+
  
 
Обход таблицы вверх по иерархии. В таблице нам важны поля "name" - имя и "parent" - родительская запись.
 
Обход таблицы вверх по иерархии. В таблице нам важны поля "name" - имя и "parent" - родительская запись.
  
Выводит типа "КОРЕНЬ >> СЫН >> СЫН >> ... >> СЫН >> ТЕКУЩИЙ ЭЛЕМЕНТ"
+
Выводит типа "КОРЕНЬ >> СЫН >> СЫН >> >> СЫН >> ТЕКУЩИЙ ЭЛЕМЕНТ"
  
 
<pre>
 
<pre>

Версия 02:10, 13 января 2011

Данный документ оставлен в живых по исключительно историческим причинам. Это один из первых скриптов, которые я написал на PHP — тот самый «Index.Generate и Index.Compile», о котором говорится тут. На этом работала моя первая-первая домашняя страничка на h11.ru.

Ну что тут сказать?

  • Я тогда не знал, что в базе данных бывают индексы и первичные ключи, и использовал её как INI-файл.
  • Любовь к зюкам-хрюкам началась давно и сразу.
  • Это ж надо было столько кода понаписать…

Написано 2006-04-24.


VMX index.php 1.06

index.php — php-скрипт, который строит HTML код (на самом деле — вообще любой файл, но предназначен именно для генерации HTML-кода), основываясь на шаблоне и данных БД MySQL.

Зачем оно надо? Веб-дизайнер может создать ПУСТОЙ макет страницы, расставить управляющие коды и получить готовый к использованию шаблон. Далее к сайту создаётся БД (например, через gendbv2.php), и весь сайт работает сгенерированный через index.php.

Естественно, шаблонов может быть несколько — например, один для страницы с новостями, другой для страницы с файлами, третий ещё для чего-нибудь… Несколько шаблонов вместе образуют тему. Темы располагаются в подпапке themes/ относительно папки с index.php. Каждая тема лежит в отдельной подпапке: themes/<имя_темы>

Названия всех таблиц в БД, относящихся к сайту, по умолчанию начинаются с Pages, за это отвечает переменная $PagesTable (см. config.php). Во всех таблицах должна присутствовать индексная колонка «Id». Других требований к таблицам НЕТ. При переходе в любую таблицу командой $$jmp к её имени в начало дописывается Pages (вернее, переменная $PagesTable).

Конфигурационные переменные $db, $dbhost, $dbuser, $dbpwd — отвечают за имя БД, хост, на котором расположен сервер MySQL, имя пользователя MySQL и пароль MySQL соответственно.

index.php следует вызывать так: «index.php?id=<ИНДЕКС_СТРАНИЦЫ_В_ТАБЛИЦЕ_СТРАНИЦ_БД>&t=<ШАБЛОН>&theme=<ТЕМА>». Ни один из этих трёх параметров не является обязательным. По умолчанию id=1, theme=default, t=default, т.е шаблон по умолчанию — это «templates/default/default».

Шаблон — любой текстовый файл, в который добавлены управляющие коды.

В самом начале обрабатываются все команды «$$include$FILE» — см.ниже.

Потом обрабатываются подстановки типа:

<<i>> (i — число)
подставит значение Id’а записи таблицы, обрабатываемой на уровне вложенности i (0 — весь файл, 1,2,… — циклы)
<<#>>
подставит значение текущей обрабатываемой записи в таблице.
<<#STR>>
выводится номер записи, обрабатываемой на текущем+длина(STR) уровне вложенности
<<#-STR>>
выводится номер записи, обрабатываемой на текущем-1-длина(STR) уровне вложенности
<<##STR>>
выведется «#STR»
<<%>>
номер текущей итерации
<<@>>
индекс последней вставленной в БД записи (используется функция mysql_insert_id())

Имя переменной — далее VAR — это одно из:

просто строка
тогда это не переменная
{{VarName}}
сохранённая переменная VarName. Также для index.php {(VarName)} означает то же самое.
{VarName}
тогда это будет глобальная переменная PHP VarName; предопределённые:
*{Inum}
номер текущей итерации цикла, начиная с нуля
{amy_query_count}
  • количество произведённых SQL запросов
[FieldName]
тогда значение будет прочитано из поля FieldName текущей обрабатываемой записи. Эквивалентно [<<#>>\FieldName], где # — номер текущего уровня вложенности.
[Id\FieldName]
тогда значение будет прочитано из поля FieldName записи с индексом Id текущей таблицы.
[#Special\FieldName]
специальные значения. Вызывают SQL-запрос вида «SELECT Special(FieldName) FROM `TableName`».

Примеры:

[#COUNT\*]
количество записей в текущей таблице
[#MAX\Id]
максимальное значение поля Id в текущей таблице

Если перед любым из этих выражений поставить \, то она преобразуется через mysql_escape_string. Если @, то как html_pbr (преобразование переводов строк в <br />, а символов &<>"' — в HTML-entity). Если & — тогда преобразование заключается в удалении всех HTML тэгов кроме разрешённых в config.php. Если поставить \\, то это воспримется просто как символ \. Если @@ — то @. Если && — просто &.

Итак: вне команд $VAR$ — подстановка переменной. В командах — смотрите отдельно :)

Команды (ставятся на отдельную строку):

$$sql$QUERY
выполнить запрос QUERY. QUERY — может содержать подстановки переменных.
$$qor$QUERY
выполнить запрос QUERY и обработать результат в цикле.
$$wor$table$where
частный случай предыдущего:
SELECT * FROM `table` WHERE where
Значит — обработать в цикле все записи table, соответствующие WHERE-выражению where. where — это вся часть строки после $$wor$. Примечание: Если не хотите никуда переходить — напишите $$wor${table}$… Т.к глобальная переменная PHP table — это текущая таблица.
Примеры:
$$wor$1
обойти всю таблицу.
$$wor$Id>=1 AND Id<=5
обойти записи с индексами от 1 до 5.
$$wor$Id>=1 LIMIT 5
обход 5 записей, начиная с индексов больших 1. (см. 9 подсказку)
$$for$table$VAR1$VAR2
Частный случай цикла wor. Обход Id>=VAR1 AND Id<VAR1+VAR2
$$filter$VAR1$VAR2
Цикл, выбирающий из текущего кэша записи, у которых значение колонки VAR1 равно VAR2
$$whileXX$VAR1$VAR2
Цикл повторяющий своё тело до тех пор, пока VAR1 XX VAR2 истинно
$$cache$WHERE
Кэширование текущей записи или всех записей, соответствующих WHERE
$$cache
Кэширование текущей записи
$$cacheassign$LEVEL
присвоить текущий кэш равным кэшу уровня LEVEL
$$cleancache
Очистка кэша
$$exit
Останов или выход из итерации цикла
$$break
Выход из цикла
$$block
Начало блока (например, if'a)
Пример:
$$block
$$if<?$VAR1$VAR2
...
$$/
- это if (VAR1 < VAR2) { ... }
$$/
конец блока или тела цикла
$$ifXX$VAR1$VAR2
оператор условного выхода - проверить VAR1 XX VAR2. Если ложь, выйти. Для цикла "выйти" значит "перейти к следующей записи таблицы". Внимание! Не используйте это в циклах - будет неоптимально.
$$include$FILE
прямое включение шаблона. В FILE никаких переменных! Кроме того, эта команда ДОЛЖНА стоять на отдельной строке.
$$php$VAR
включение и выполнение php-кода из файла VAR.
$$chg$VAR
переключение на обработку записи под индексом VAR. ВНИМАНИЕ! О кэшировании новой записи она не заботится.
$$wtime
вывести текущее время выполнения.

Вниманию оптимизаторов! Следующие три команды очищают кэш.

$$jmp$VAR
перейти в таблицу с именем VAR.
$$ret
вернуться на предыдущую таблицу (история хранится)
$$aret
вернуться на главную таблицу и очистить историю переходов

Поддерживается вложенная буферизация вывода в 3 режимах - добавление в буфер справа (A+B = AB), слева (A+B = BA), или без буферизации. По умолчанию буферизация 1 уровня справа.

$$bufstartR
начать новый уровень буферизации справа
$$bufstartL
начать новый уровень буферизации слева
$$bufchgR
изменить метод буферизации на буферизацию справа
$$bufchgL
изменить метод буферизации на буферизацию слева
$$bufclear
очистить буфер текущего уровня наффиК
$$bufflush
сбросить буфер текущего уровня в буфер предыдущего уровня
$$bufstop
закончить текущий уровень буферизации и сбросить буфер в предыдущий
$$bufsave$name
сохранить буфер под именем name (обращаться потом как $Шаблон:Name$)
$$save$name$value
сохранить value как name; для index.php $$define$name$value делает то же самое.

compile.php - специфичные команды и подстановки:

$$define$name$value
сохранить value как name НА ВРЕМЯ КОМПИЛЯЦИИ. То есть, разница между $$save и $$define при компиляции такая: если написать $$save$name$value - в php-код запишется строка типа $saved_buffers [name] = value; а при использовании define значение будет подставляться прямо в то место, где используется. При этом все подстановки внутри него всё равно работают.
$$phpverbatim$filename.php
прямое включение в выходной php-код отрезка файла filename.php начиная с первого <?php и кончая первым ?>
$$phpfunc$filename.php
создастся функция с телом, равным отрезку файла filename.php начиная с первого <?php и кончая первым ?>, и тут же и вызовется
подстановка {(VarName)}
это как раз подстановка define’ов.

Логические операции (XX в $$if и $$while):

<
численное/лексикографическое сравнение на "меньше".
<=
численное/лексикографическое сравнение на "меньше или равно".
==
численное/лексикографическое сравнение на "равно".
!=
численное/лексикографическое сравнение на "не равно".
>=
численное/лексикографическое сравнение на «больше или равно».
>
численное/лексикографическое сравнение на «больше».
?
проверка, задана ли глобальная переменная PHP VAR1 — true если задана и непуста как строка
!?
отрицание «?»

Причём, в именах полей/идентификаторах также можно использовать <<i>> т.к всегда и сначала подставляются <<i>>.

Для оптимизации работы с БД записи кэшируются. Вначале, при запуске скрипта кэшируется не больше одной записи с Id равным $recid. Однако потом, в циклах, кэшируется сразу много записей. А именно — все, подходящие под границы цикла. В принципе, это может создать проблемы с памятью при обработке действительно крупных объёмов данных. Но на практике всё будет нормально.

Далее, compile.php. Как и видно из названия — этот скрипт компилирует шаблоны. То есть, создаёт PHP скрипты, делающие то же самое, что и index.php при интерпретации шаблонов. Вызывать его следует так же, как и index.php: /compile.php[?t=TEMPLATE][&theme=THEME]. При этом выход запишется в файл THEME_TEMPLATE.php в ту же папку, где лежит сам compile.php. Шаблоны он берёт оттуда же, откуда и index.php. При работе compile.php меняет все вхождения /index.php[?t=...][&theme=...] на вызов соответствующего откомпилированного скрипта.

Кроме того, вы можете указать дополнительные опции компилятора шаблонов:

&no_cache_when_reading
не обновлять кэш, если в нём нету переменной — немного оптимизирует скрипт по времени работы. Не обновлять — то есть не записывать код, производящий обновление кэша.
&generate_comments
генерировать комментарии — записывать каждый кусок файла в комментарии перед командами, ему соответствующими.
&default_theme=DEF
переименовать тему по умолчанию в DEF, если DEF пусто — тогда имена файлов будут просто TNAME.php.
&default_t=DEF
переименовать шаблон по умолчанию в DEF.
Переименование влияет на имена подставляемых вместо /index.php[?t=…][&theme=…] скриптов и на имя выходного скрипта.

Также для удобства создан скрипт compileui.php — пользовательский интерфейс компилятора. Заполните поля ввода и нажмите кнопку «компилировать тему» — после этого все шаблоны темы с названием, заданным в поле «тема», кроме шаблонов, лежащих в файлах с расширениями, откомпилируются и будут помещены в ту же папку, где лежит compileui.php. compile.php для работы через compileui.php должен находиться в корневой папке сервера.

  • Поле «тема» задаёт компилируемую тему.
  • Поле «тема default=" - аналог параметра компилятора &default_theme=DEF.
  • Поле "шаблон default=" - аналог параметра компилятора &default_t=DEF.
  • Флажок "Управляю кэшем вручную и корректно» — аналог опции компилятора &no_cache_when_reading.
  • Флажок «Генерировать комментарии к коду» — аналог опции компилятора &generate_comments.

Подсказки разработчикам

  1. Во-первых, в принципе, скрипт не ориентирован на ввод информации в БД, хотя он вполне может осуществляться — через команду $$sql, формы и глобальные переменные PHP. Все переменные запроса импортируются с req_ перед названием. То есть, если у вас на странице есть что-то вроде <input type="text" name="helloworld" maxlength=256> — то после отправки формы текст из этого поля ввода появится как переменная ${req_helloworld}$. Соответственно, вы можете использовать её в SQL запросе.
  2. В ту же тему: если ваш движок должен поддерживать некую конфигурацию через config.php — допишите в него между «<?php" и "?>» строки вида «$YOUR_VAR_NAME = YOUR_VAR_VALUE;» и далее сможете использовать их как ${YOUR_VAR_NAME}$.
  3. Чтобы включить форму аутентификации — нужно написать ОТДЕЛЬНО два php-скрипта — один для вывода формы или приветствия, а другой для обработки команд аутетификации. Связано это с тем, что протокол HTTP не позволяет передавать HTTP Cookies иначе, чем как ДО НАЧАЛА любого вывода скрипта. То есть — скрипт-обработчик команд «вход» и «выход» должен быть включён в САМОМ начале шаблона — на первой строке. Так же и с любыми скриптами, работающими с HTTP Cookies. Скрипт, выводящий форму или приветствие, вы можете размещать где угодно. UPD: ЛИБО (проще) в начале включаете буферизацию справа ($$buf+r), и выводите страницу.
  4. Старайтесь оптимизировать работу с базой данных: именно MySQL является узким местом в производительности. В принципе, для этого много умения не надо :) но, например: допустим, есть таблица с категориями файлов, которые образуют иерархическую структуру. Категорий файлов, как правило, немного, и их можно кэшировать прямо все, потому что иерархический обход с использованием while — штука, которая без ручного кэширования кушать запросы будет только так. Соответственно, перед while переходим в требуемую таблицу, кэшируем все её записи — $$cache$1, и дальше начинаем while.
  5. Цикл идентифицирует итерации по колонке Id. Так что если её в строках результата не будет, она, хы, доопределится :) причём — отрицательными значениями. Чтобы было ясно, что это 'несуществующие' индексы.
  6. Т.к $$sql требуется обычно для SQL-выбора из нескольких таблиц, то имейте ввиду, что в теле цикла, мягко говоря, не рекомендуется использовать записи с индексами кроме выбранных самим запросом — если выбор шёл из одной таблицы они, при отсутствии в кэше, подгрузятся, а здесь «угадать» таблицу скрипт не сможет.
  7. Не используйте в циклах операторы условного выхода — неоптимально.
  8. При включении PHP-скрипта командой $$php скрипт будет «по умолчанию» видеть следующие 3 глобальные переменные:
    • $link — MySQL соединение с БД;
    • $result — MySQL результат запроса;
    • $table — имя текущей таблица;
    Осторожно обращайтесь с этими переменными во включаемых php-скриптах. При их повреждении страница, скорее всего, отобразится некорректно.
  9. «$$wor$Id>=1 AND Id<=5" и "$$wor$Id>=1 LIMIT 5» — это разные вещи. Потому что если, например, индексов >=1 и <=5 нету, а начинаются они, например, лишь с 7, то первая команда не обойдёт ничего, а вторая обойдёт <= 5 записей, начиная с 7-ой.
  10. Вообще говоря - $$include$FILE - это не команда. Собственно интерпретатор даже не видит такие команды - они обрабатываются до него и прозрачно.

Примеры

Обход таблицы вверх по иерархии. В таблице нам важны поля "name" - имя и "parent" - родительская запись.

Выводит типа "КОРЕНЬ >> СЫН >> СЫН >> … >> СЫН >> ТЕКУЩИЙ ЭЛЕМЕНТ"

$$block
$$jmp$HierTable
$$cache$1
$$bufstartL
$[name]$
$$chg$[parent]
$$while!=$<<#>>$0
$[name]$ >>
$$chg$[parent]
$$/
$$bufstop
$$ret
$$/

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

Обход таблицы вниз по иерархии. Если было дерево вида:

    0
   / \
  1   2
 / \
3   4

ТО выведется 0 1 3 4 2.

$$block
	$$jmp$HierTable
	$$cache$1 order by `Id`
	$$save$lastparent$0
	$$save$lastwalked$0
	$$while>$<<#>>$0
		$$save$walking${{lastparent}}
		$$filter$Parent${{lastparent}}
			$$if>$<<#>>${{lastwalked}}
			$$save$walking$<<#>>
			$$break
		$$/
		$$chg${{walking}}
		$$save$lastwalked${{walking}}
		$$save$mif$0
		$$block
			$$if!=${{walking}}${{lastparent}}
			$$save$mif$1
		$$/
		$$block
			$$if>${{mif}}$0
			$[Id]$
			$$save$lastparent${{walking}}
			$$save$lastwalked$0
		$$/
		$$block
			$$if==${{mif}}$0
			$$if>${{lastparent}}$0
			$$save$lastparent$[Parent]
		$$/
	$$/
	$$ret
$$/