Организация модулей системы |  |
1. Основные определения
1.1. Модуль - один или несколько файлов *.php,
*.xml и, возможно, один файл *.html,
предназначенные для решения некоторой общей подзадачи.
1.2. Библиотека - множество модулей,
решающих одну общую задачу.
Библиотеки также допускается называть словом «служба».
1.3. Набор библиотек - все библиотеки системы.
1.4. Шаблон - один файл *.xml.
1.5. Набор шаблонов - множество шаблонов,
образующие в целом единый дизайн («скин»).
2. Правила именования
2.1. Набор библиотек всегда называется словом libs.
2.2. Имя одной библиотеки состоит из одного слова и
всегда записывается всеми маленькими буквами. Исключение составляет
использование этого имени в константах, когда имя библиотеки записывается
всеми большими буквами.
2.3. Имя модуля состоит из двух и более слов.
Исключение составляет модуль общих функций, имя которого состоит из одного слова.
Все слова записываются маленькими буквами, в качестве разделителя используется
символ “.” (точка).
В качестве первого слова всегда выбирается имя библиотеки, которой принадлежит
этот модуль. Имена модулей организованы иерархически.
2.4. Имя шаблона состоит из двух и более слов.
Все слова записываются маленькими буквами, в качестве разделителя используется
символ “.” (точка).
В качестве первого слова всегда выбирается имя библиотеки, которой принадлежит
этот шаблон. Имена шаблонов организованы иерархически.
2.5. Имена функций состоят из двух и более слов.
Все слова записываются маленькими буквами, в качестве разделителя используется
символ “_” (подчеркивание).
Имена функций организованы иерархически.
Первые слева слова соответствуют всем словам имени модуля, в котором эта
функция определена.
Например, в модуле “passport.privilege” имена всех функций
начинаются на “passport_privilege_”.
Длина имени функции может быть такой же, как и длина имени модуля только в
случае, если эта функция - единственная в этом модуле.
2.6. Имена констант состоят из двух и более слов.
Все слова записываются большими буквами, в качестве разделителя используется
символ “_” (подчеркивание).
В качестве первого слова имени выбирается имя библиотеки, в которой определена
эта константа.
Имена констант организованы иерархически.
2.7. В каждой библиотеке может быть объявлено не более одной глобальной
переменной. Имя этой переменной состоит из двух слов. Первое - соответствует
имени библиотеки, второе - “info”. В качестве разделителя
используется подчеркивание. Например, “$cms_info”,
“$passport_info”. Эта переменная является ассоциативным
массивом, в котором записаны все нужные данные.
2.8. Каждому модулю может соответствовать один файл *.html.
Имя этого файла соответствует имени модуля, но без первого слова
(без имени библиотеки).
Все слова записываются маленькими буквами, в качестве разделителя
используется символ “-” (минус).
Например, модулю news.admin.item.remove
соответствует файл
admin-item-remove.html
Для организации ЧПУ и избежания конфликтов с “content negotiation”
допускается введение в имя файла *.html префикса “do-”.
В некоторых случаях в качестве разделителя используется символ “.” (точка) -
это устаревший вариант.
3. Состав библиотеки
3.1. Все модули являются необязательными.
Приведенные ниже имена являются зарезервированными и не должны использоваться
для других целей.
3.2. Модуль общих функций. В этом модуле записываются функции, которые
используются разными модулями этой библиотеки или могут использоваться в
других библиотеках. Имя этого модуля состоит из одного слова и соответствует
имени библиотеки.
3.3. Модуль функций уровня проверки возможностей.
Имя этого модуля состоит из двух слов.
Второе слово - “can”.
3.4. Модуль проверки целостности системы.
Имя этого модуля состоит из двух слов.
Второе слово - “check”.
В этом модуле содержится скрипт для проверки корректности настроек и
проверки целостности базы данных.
3.5. Модуль очистки базы данных.
Имя этого модуля состоит из двух слов.
Второе слово - “clear”.
В этом модуле содержатся функции для удаления сущностей из базы данных.
Эти функции поддерживают целостность базы данных.
Если удаляется родительская сущность, то удаляются и все дочерние сущности.
3.6. Модуль создания базы данных.
Имя этого модуля состоит из двух слов.
Второе слово - “db”.
В этом модуле содержится скрипт для создания базы данных.
3.7. Модуль интерфейса библиотеки на главной странице сайта.
Имя этого модуля состоит из двух слов.
Второе слово - “main”.
В этом модуле обычно содержится интерфейс библиотеки для главной страницы сайта.
Например, в форуме - это список последних сообщений форума,
в службе новостей - список последних новостей, в ротаторе - форма для выбора
случайного элемента в зависимости от раздела ротатора.
3.8. Модуль системных настроек.
Имя этого модуля состоит из двух слов.
Второе слово - “options”.
В этом модуле записываются определения констант.
В качестве значений этих констант записываются значения по умолчанию.
Эти значения могут быть переопределены в глобальных настройках.
3.9. Модуль настроек пользователя.
Имя этого модуля состоит из двух слов.
Второе слово - “setup”.
В этом модуле содержится обработчик части меню, к которому зарегистрированный
пользователь может получить, выбрав пункт меню «Настройки» своего
персонального меню.
3.10. Модуль инициализации.
Имя этого модуля состоит из двух слов.
Второе слово - “startup”.
Этот модуль загружается при обращении к любой странице сайта и предназначен
либо для выполнения некоторой инициализации модуля (например, модуль
mysql.startup устанавливает соединение с базой данных;
модуль cms.startup загружает информацию о текущем языке и
текущем наборе шаблонов) либо для выполнение некоторых периодических действий
(например, модуль news.startup предназначен для периодической
рассылки новостей; stat.startup - добавляет запись в журнал
статистики).
Этот модуль не загружается автоматически при его создании.
Если Вы создаете новый такой модуль, то его следует регистрировать в
модуле all.init.
3.11. Модуль управления гиперссылками.
Имя этого модуля состоит из двух слов.
Второе слово - “url”.
В этом модуле содержатся функции для генерации ссылок на файлы
*.html, принадлежащие этой библиотеке.
3.12. Все файлы *.html, принадлежащие одной библиотеке,
хранятся в каталоге html внутри каталога библиотеки.
В этом каталоге всегда находится самая свежая версия этих файлов.
Во всех остальных местах (например, в каталоге public_html)
содержатся копии этих файлов.
3.13. Если библиотека содержит в себе хотя бы один файл
*.html, то в этой библиотеке также есть один файл
inc.php, который загружается из всех файлов
*.html.
Этот файл предназначен для загрузки ядра системы.
3.14. В каталоге xml содержится часть системного набора шаблонов,
соответствующая этой библиотеке.
В системном наборе шаблонов обязательными являются все шаблоны библиотеки,
поскольку именно эти шаблоны используются, если шаблон не найден
в текущем наборе шаблонов; в любом несистемном наборе шаблонов все шаблоны
являются необязательными.
3.15. В каталоге xml-<template> содержится часть
несистемного набора шаблонов <template>,
соответствующая этой библиотеке.
4. Состав модуля
4.1. Все файлы модуля являются необязательными.
Приведенные ниже имена являются зарезервированными и не должны использоваться
для других целей.
4.2. Файл *.html - представляет собой интерфейс модуля,
доступный из интернета. В этом файле содержится подключение файла
inc.php и соответствующего управляющего файла.
Одному модулю соответствует один файл *.html.
Если требуется несколько таких файлов, то используется несколько модулей.
4.3. Управляющий файл - содержит в себе скрипт для управления
потоком выполнения. Имя управляющего файла совпадает с именем модуля, к
которому добавляется окончание .html.php.
4.4. Функции модуля описываются в отдельном файле.
Этот файл называется «файл с функциями».
Обычно в одном файле описываются все функции модуля.
Обычно одному модулю соответствует один файл с функциями.
В этом файле не содержится ничего кроме функций.
Имя файла с функциями совпадает с именем модуля, к которому добавляется
окончание .php.
4.5. Загрузчик шаблона предназначен для подготовки данных и загрузки
шаблонов.
Имя файла с функциями совпадает с именем модуля, к которому добавляется
окончание .xml.php.
В большинстве случаев одному модулю соответствует один загрузчик шаблона,
но в некоторых сложных случаях это правило может нарушаться.
4.6. Шаблоны - файлы *.xml, в которых содержится весь
выводимый в браузер код. Только в очень редких и очень сложных случаях
допускается вывод без использования шаблонов.
4.7. Управляющий шаблон - шаблон, в котором происходит загрузка
стандартного заголовка, тела генерируемого документа и стандартного окончания.
Имя этого шаблона совпадает с именем модуля, к которому добавляется окончание
*.html.xml. Для этого шаблона нет загрузчика шаблона.
4.8. Обычно один модуль обрабатывает два вида запросов -
GET и POST.
Распознавание вида запроса происходит в управляющем файле.
При запросе GET обычно генерируется возвращаемый в бразуер код.
При запросе POST обычно модифицируется база данных и происходит перенаправление.
4.9. Обработчик данных, переданных методом
GET - это функция, которая описана в файле с функциями.
Имя это функции соответствует имени модуля.
В качестве последнего слова в имя функции добавляется слово “load”.
Эта функция вызывается всегда, не зависимо от того, какой вид запроса
обрабатывается, поскольку переменные могут передаваться методом
GET при любом виде запроса.
Эта функция проверяет исходные данные и возвращает код ошибки в случае ошибки.
Эта функция может загружать дополнительные данные,
которые необходимы для проверки правильности исходных данных, но которые могут
быть использованы в загрузчике шаблонов - это выполняется для того, чтобы
в загрузчике шаблонов не загружать эти данные повторно.
4.10. Обработчик данных, переданных методом
POST - это функция, которая описана в файле с функциями.
Имя это функции соответствует имени модуля.
В качестве последнего слова в имя функции добавляется слово “save”.
Эта функция вызывается при обработке запроса POST.
Эта функция проверяет исходные данные и модифицирует базу данных или
возвращает код ошибки.
4.11. Для удаления сущностей из базы данных обычно создается отдельный
(дочерний) модуль в составе данного (родительского) модуля.
Этот модуль называется «модуль удаления».
Имя этого модуля соответствует имени родительского модуля.
В качестве последнего слова имени дочернего модуля добавляется слово
“remove”.
Функция для удаления сущностей обычно описывается в файле с функциями
родительского модуля.
5. Примеры стандартных шаблонов файлов модулей
5.1. Стандартный комментарий.
Стандартный комментарий содержится в начале каждого файла системы.
Стандартный комментарий обычно содержит в себе три строки:
- имя файла
- имя разработчика, дата создания, ссылки
- краткое описание
5.2. Проверка лицензии и целостности системы.
Во всех файлах *.php системы содержится раздел, предназначенный
для проверки лицензии и целостности системы.
Этот раздел начинается со строки:
// LICENSE-VERIFICATION-BEGIN
И заканчивается строкой:
// LICENSE-VERIFICATION-END
Размер раздела - ровно одна строка.
Содержимое этого раздела генерируется автоматически.
Для проверки целостности системы выполняются тесты, проверяющие, например, что
модули загружаются только в установленном порядке, что были выполнены
необходимые этапы инициализации системы.
Для проверки лицензии выполняются дополнительные тесты, проверяющие
возможность использования этих модулей. Эти тесты зависят, например,
от параметров лицензии (лицензией задается список разрешенных модулей),
от целостности лицензии, от текущей даты и пр.
Проверка лицензии выполняется только в платной версии системы, если
система поставляется в зашифрованном виде.
Во всех остальных случаях выполняется только проверка целостности системы.
На момент разработки этот раздел является обязательным для всех файлов,
расположенных за пределами каталога libs
(будем называть эти файлы «внешними файлами»).
Система будет работать корректно только если
этот раздел удален во всех файлах системы. Если в некоторых файлах
этот раздел существует, то этот раздел должен быть во всех внешних файлах.
Для файлов внутри каталога libs содержимое этого раздела может
быть удалено во время разработки системы.
Удаление самого раздела не допускается.
5.3. Файл *.html.
<?php // public_html/jewel/account-metal.html // (c) Yuri Popoff, Nov 2005, popoff.donetsk.ua // A script to edit a list of rests in a given account
// LICENSE-VERIFICATION-BEGIN function inc_public_html($t) { return sha1('*МемЁжюr'.$t); } include('inc.php'); $t=sha1(microtime().'EчфЯющNr'.mt_rand(0,0x7fffffff)); if(!function_exists('inc_inc_inc')|| inc_inc_inc($t)!==sha1('Prм{G~]м'.$t)|| !function_exists('inc_libs')|| inc_libs($t)!==sha1('Ф]|Т|0ДW'.$t)|| !function_exists('lib_include_once')|| !function_exists('lib_include') ) die('1504'); // LICENSE-VERIFICATION-END
lib_include('jewel.account.metal.html'); die('Error 1500.');
?>
5.4. Управляющий файл.
Пример управляющего файла с комментариями приведен на рисунке.
<?php // libs/jewel/jewel.account.metal.html.php // (c) Yuri Popoff, Nov 2005, popoff.donetsk.ua // A script to edit a list of rests in a given account
// LICENSE-VERIFICATION-BEGIN
// LICENSE-VERIFICATION-END
//Выполняем стандартную инициализацию lib_include('all.init');
//Подключаем требуемые модули lib_include_once('http.load'); lib_include_once('jewel.can'); lib_include_once('jewel.url'); lib_include_once('jewel.account.metal');
//Загружаем данные, переданные метдом GET $data=array('k_agent' => 0,'page' => 0); http_load_get($data); switch($r=jewel_account_metal_load($data)) { case 0: break; case 1: //k_agent not exists $msg=m('k_agent not exists|jewel.account.metal'); break; case 2: //access denied: jewel.account.metal $msg=m('access denied: jewel.account.metal|jewel.account.metal'); break; default: trigger_error('An invalid value returned: '.$r); $msg=m('An internal server error has occured.'); }
if($r) { debug_status($r,'jewel-account-metal-get-error'); lib_include('mp.redirect.error.xml',$msg,URL_JEWEL,m('jewel|title')); debug_exit(); }
//Загружаем данные, переданные методом POST $pdata=array('f_metal' => 0,'k_metal' => 0,'s_comment' => ''); if(http_load_post('jewel-account-metal-add',$pdata)) { //Если выполняется запрос вида POST, то модифицируем базу данных switch($r=jewel_account_metal_save($pdata,$data)) { case 0: //статус - нужен для выполнения автоматических тестов debug_status(0,'jewel-account-metal-post-ok'); //Перенапарвляем и выводим сообщение об удачном выполнении операции lib_include('mp.redirect.ok.xml',m('Metal have been added successfully to the account.|jewel.account.metal'),jewel_url_account_metal($data['k_agent']),m('jewel|title')); debug_exit(); break; case 1: //k_metal not exists $msg=m('k_metal not exists|jewel.account.metal'); break; case 2: //f_metal is invalid $msg=m('f_metal is invalid|jewel.account.metal'); break; default: //Стандартное сообщение об ошибке во всех остальных случаях // Обязательно вызывать trigger_error trigger_error('An invalid value returned: '.$r); $msg=m('An internal server error has occured.'); } debug_status($r,'jewel-account-metal-post-error'); lib_include('mp.redirect.error.xml',$msg,jewel_url_account_metal($data['k_agent']),m('jewel|title')); // debug_exit выполняет дополнительные операции, // необходимые при завершении скрипта в режиме отладки debug_exit(); }
debug_status(1,'jewel-account-metal-get-ok');
//Формируем заголовок, строку "где я нахожусь" mp_title(m('jewel|title')); mp_place(m('jewel|place'),URL_JEWEL); mp_place(m('account.metal|jewel.place')); mp_place(cms_htmlspecialchars($data['s_agent'])); //информация для подсветки главного меню mp_current('jewel');
//загружаем управляющий шаблон template_echo('jewel.account.metal.html',$data); //В управляющих файлах не допускается выход после последней строки - // в конце всегда нужно ставить debug_exit() debug_exit();
?>
5.5. Стандартные коды ошибок.
Функции для загрузки данных, модификации базы данных и удаления записей из базы
данных возвращают код результата выполнения (целое число). Стандартными
являются следующие значения:
-1 - внутренняя ошибка. Например, ошибка при выполнения запроса к базе
данных или в качестве аргумента передан не массив.
0 - успех. Действие успешно выполнено.
Значение больше нуля - код ошибки.
Замечание: Функции должны быть написаны таким образом, чтобы ситуации,
когда эта функция возвращает код ошибки “-1” всегда были невозможными.
Например, в случае с SQL-запросами функция должна всегда генерировать такие
SQL-запросы, которые при любых исходных данных являются правильными.
5.6. Файл с функциями.
Пример файла с функциям, в котором записаны заготовки для трех стандартных
функций (load, save, remove) приведен
на рисунке.
<?php // libs/jewel/jewel.account.metal.php // (c) Yuri Popoff, Nov 2005, popoff.donetsk.ua // A script to edit a list of rests in a given account
// LICENSE-VERIFICATION-BEGIN
// LICENSE-VERIFICATION-END
lib_include_once('jewel.can'); lib_include_once('jewel.options');
//Загрузчик данных GET function jewel_account_metal_load(&$data) { //return values: // -1 - internal error // 0 - ok // 1 - access denied: jewel.account.metal // 2 - k_agent not exists
if(empty($data)||!is_array($data)) { // Такого быть не может, но проверку все равно делаем trigger_error('$data is not an array'); return -1; }
// Проверяем привилегии if(!jewel_can_account_metal()) return 1;
//Проверяем суещствование записи // Все требуемые элементы массива всегда существуют, но проверку на // существование все равно выполняем // Для проверки, является идентификатор правиьным // (положительное целое число) можно использовать функцию is_id() if(empty($data['k_agent'])||!is_id($data['k_agent'])) return 2; // mysql_query_single - удобно, если нужно выбрать одну строку $r_agent=mysql_query_single(" select 1 from ".JEWEL_TABLE."agent where k_agent=".$data['k_agent']." ",true); if(!$r_agent) return 1;
// Загружаем дополнительные данные // Эти данные могут потребоваться в управляющем файле, например, для // формирования заголовка $data['s_agent']=cms_select('s_title',JEWEL_TABLE.'agent_cms','k_agent='.$data['k_agent']);
return 0; }
// Модификация базы данных function jewel_account_metal_save($pdata,$gdata) //return values: // -1 - internal error // 0 - ok // 1 - k_metal not exists // 2 - f_metal is invalid
{ if(empty($pdata)||!is_array($pdata)) { trigger_error('$pdata is not an array'); return -1; } if(empty($gdata)||!is_array($gdata)) { trigger_error('$gdata is not an array'); return -1; }
// Проверку данных, переданных методом GET здесь можно не осуществлять.
if(empty($pdata['k_metal'])||!is_id($pdata['k_metal'])) return 1; $r_metal=mysql_query_single("select s_type from ".JEWEL_TABLE."metal where k_metal=".$pdata['k_metal'],true); if(!$r_metal) return 1;
// Здесь записываются проверки других данных
// Только если все данные - правильные - выполняем запрос на модификацию // Даже после всех проверок все пришедшие от пользователя данные следует // считать опасными if(!mysql_query_log(" insert into ".JEWEL_TABLE."account_metal set k_agent='".mysql_real_escape_string($gdata['k_agent'])."', dt_date='".date_unix_mysql(time())."', f_metal='".mysql_real_escape_string($pdata['f_metal'])."', k_metal='".mysql_real_escape_string($pdata['k_metal'])."', k_lang=".cms_language_id().", s_comment='".mysql_real_escape_string($pdata['s_comment'])."', uid=".passport_uid()." ")) return -1;
// Изменили базу - не забываем очистить кеш cms_cache_clear('jewel jewel.account jewel.account.'.$data['k_agent']);
return 0; }
function jewel_account_metal_remove($data) { //return values: // -1 - internal error // 0 - OK // 1 - k_agent not exists // 2 - Access denied: jewel.agent.remove // 3 - removal code is invalid
if(empty($data)||!is_array($data)) { trigger_error('$data is not an array'); return -1; } //Проверяем элемент на существование if(empty($data['k_agent'])||!is_id($data['k_agent'])) return 1; $r=mysql_query_single("select 1 from ".JEWEL_TABLE."account_metal where k_agent=".$data['k_agent']); if(empty($r)) return 1;
// Проверяем привилегии if(!jewel_can_account_metal_remove()) return 2;
// Проверяем код удаления - для защиты от прямых ссылок $c=hash_str(session_id().passport_uid().'jewel.account.metal'.$data['k_agent'].ADDR_SECRET); if(empty($data['c'])||$data['c']!==$c) return 3;
// Эта функция только выполняет проверки. // Непосредственно удаление производится в модуле очистки, // который следит также за целостностью базы данных lib_include_once('jewel.clear'); if(!jewel_clear_account_metal($data['k_agent'])) return -1;
// Изменили базу - не забываем очистить кеш cms_cache_clear('jewel jewel.account jewel.account.'.$data['k_agent']);
return 0; }
?>
5.7. Загрузчик шаблона.
Пример загрузчика шаблона (редактор таблицы) с комментариями приведен на рисунке.
<?php // libs/jewel/jewel.account.metal.xml.php // (c) Yuri Popoff, Nov 2005, popoff.donetsk.ua // A form to edit a list of rests in a given account
// LICENSE-VERIFICATION-BEGIN
// LICENSE-VERIFICATION-END
//Все данные всегда передаются в виде массива через аргумент 1 $data=func_get_arg(1);
// Подключаем требуемые модули lib_include_once('a.money');
$r=mysql_query_log(" select ".JEWEL_TABLE."account_metal.* from ".JEWEL_TABLE."account_metal where ".JEWEL_TABLE."account_metal.k_agent=".$data['k_agent']." order by k_log desc "); $a_item=array(); for($i=0;$i<mysql_num_rows($r);$i++) { $f=mysql_fetch_assoc($r);
// Выполняем разные преобразования для подготовки всех элементов
$f['f_metal_rest']=a_money_format($f['f_metal_rest'],7,3); $f['f_metal']=a_money_format($f['f_metal'],7,3); $f['index']=$i+1; $f['s_type']=htmlspecialchars($f['s_type']); if(!empty($f['k_order'])) { $f['url-order']=jewel_url_order_item($f['k_order']); } $a_item[]=$f; }
// Загружаем сам шаблон и передаем в него все необходимые данные
return template_load('jewel.account.metal',array( 'a_item' => $a_item, 'can-agent-view' => jewel_can_agent_view(), 'k_agent' => $data['k_agent'], 's_agent' => cms_htmlspecialchars($data['s_agent']), 'url-agent-view' => jewel_url_agent_view($data['k_agent']) ));
?>
5.8. Шаблон
Обычно шаблоны не вызывают затруднений.
Синтаксис шаблонов описан в разделе “cms.template”.
Пример управляющего шаблона со стандартным комментарием приведен на рисунке.
<?xml version="1.0" encoding="windows-1251" ?>
<!--
jewel.account.metal.html.xml
(c) Yuri Popoff, Dec 2005, popoff.donetsk.ua
A form to view metal account log for a given agent
-->
<template clear="1">
<load src="mp.header" />
<load src="jewel.menu" />
<load src="jewel.account.metal" />
<load src="mp.footer" />
</template>
Последняя модификация: 22.01.06 21:50 q Не проходите мимо! Оставьте Ваш комментарий в форуме! >>> Цитирование материалов моего сайта приветствуется! при условии видимой действующей! гиперссылки на мой сайт. [Ссылки] Если Вы нашли опечатку на этой странице, пожалуйста, выделите ее мышью и нажмите Ctrl+Enter. Сделаем язык чище! (c) Yuri Popoff, 2004 - 2008, popoff.donetsk.ua, style.donetsk.ua |
|