\----------------------/ 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 - число) - подставит значение 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. Если & - тогда преобразование заключается в удалении всех 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 -- $$phpfunc$filename.php - создастся функция с телом, равным отрезку файла filename.php начиная с первого , и тут же и вызовется -- подстановка {(VarName)} - это как раз подстановка define'ов. Логические операции (XX в $$if и $$while): < - численное/лексикографическое сравнение на "меньше". <= - численное/лексикографическое сравнение на "меньше или равно". == - численное/лексикографическое сравнение на "равно". != - численное/лексикографическое сравнение на "не равно". >= - численное/лексикографическое сравнение на "больше или равно". > - численное/лексикографическое сравнение на "больше". ? - проверка, задана ли глобальная переменная PHP VAR1 - true если задана и непуста как строка !? - отрицание "?" Причём, в именах полей/идентификаторах также можно использовать <> т.к всегда и сначала подставляются <>. Для оптимизации работы с БД записи кэшируются. Вначале, при запуске скрипта кэшируется не больше одной записи с 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_ перед названием. То есть, если у вас на странице есть что-то вроде - то после отправки формы текст из этого поля ввода появится как переменная ${req_helloworld}$. Соответственно, вы можете использовать её в SQL запросе. 2. В ту же тему: если ваш движок должен поддерживать некую конфигурацию через config.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 - это не команда. Собственно интерпретатор даже не видит такие команды - они обрабатываются до него и прозрачно. \---------/ Примеры /---------\ 1. Обход таблицы вверх по иерархии. В таблице нам важны поля "name" - имя и "parent" - родительская запись. Выводит типа "КОРЕНЬ >> СЫН >> СЫН >> ... >> СЫН >> ТЕКУЩИЙ ЭЛЕМЕНТ" $$block $$jmp$HierTable $$cache$1 $$bufstartL $[name]$ $$chg$[parent] $$while!=$<<#>>$0 $[name]$ >> $$chg$[parent] $$/ $$bufstop $$ret $$/ Фактически это просто проход по связному списку, только с той поправкой, что оно добавляет в буфер слева, а потом его выводит и отключает. 2. Обход таблицы вниз по иерархии. Если было дерево вида: 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 $$/