Всякий программист, как известно всегда стремится к отделению формы от содержания - внешнего вида от данных. В PHP наиболее распространены следующие подходы:
1) Шаблоны а-ля PHP BB. Средство не требует специальных расширений PHP, но к сожалению шаблоны статичны и их статичность приводит к тому, что в самой программе нужно заботиться о том, какой шаблон вставлять в каком случае. Это в свою очередь сводит на нет саму идею разделения данных и их внешнего представления
2) XML+XSL. Такой подход в весьма полной мере соответствует идее разделения данных и их внешнего представления, но увы требует специальных расширений PHP. Хорошо тем, кто на своем сервере хозяин-барин, то ему ничего не стоит просто поставить расширения и нужным образом настроить интерпретатор. Но как быть тем, у кого удаленный хостинг? Особеннно бесплатный хостинг. В этом случае вам если и установят расширение, то за немалые деньги (сервер придется перезапускать). Писать преобразователь самому на ПХП стоит геркулесовых трудов.
Здесь настает время вспомнить о старом забытом стандартном средстве - обработчиках теговых xml-событий.
Приведем следующий код:
Код |
<META HTTP-EQUIV="Content-Type" content="text/html; charset=windows-1251 "> <? class XMLHTML { VAR $xmlparser; VAR $tagcolor ="#800000"; VAR $datacolor ="#0000ff"; VAR $tagstack; // Стек тегов VAR $attrstack;// Стек наборов аттрибутов
function XMLHTML( ) { $this->xmlparser = xml_parser_create(); $this->tagstack = Array(); $this->attrstack = Array(); xml_set_object($this->xmlparser, &$this); xml_set_element_handler($this->xmlparser, "startTag", "endTag"); xml_set_character_data_handler($this->xmlparser, "characterData"); }
// Функция отвечает за обработку всех открывающих тегов.
function startTag($parser, $tagname, $attributes) { if ($tagname=='USERLIST') echo "<table border='7'>"; else if ($tagname=='USER') echo "<tr>"; else if ($tagname=='NAME') echo "<td>"; else if ($tagname=='EMAIL') echo "<td>"; else if ($tagname=='STATUS') echo "<td>"; array_unshift($this->tagstack,$tagname); array_unshift($this->attrstack,$attributes); }
// Функция отвечает за обработку всех символьных данных. function characterData($parser, $characterData) { GLOBAL $datacplor; if ($this->tagstack[0]=='EMAIL') echo "<a href=\"mailto:$characterData\">МЫЛИТЬ</a>"; else if ($this->tagstack[0]=='STATUS') { if ($characterData=='Администратор') echo "<font color='red'>$characterData</font>"; else echo "<font color='blue'>$characterData</font>"; } else if ($this->tagstack[0]=='NAME') echo "<font color='blue'>$characterData</font>"; //print "<font size=\"-2\" color=\"".$this->datacolor."\" face=\"arial, verdana\"> $characterData</font> <br>"; }
// Функция отвечает за обработку всех закрывающих тегов. function endTag($parser, $tagname) { GLOBAL $tagcolor; if ($tagname=='USERLIST') echo "</table>"; else if ($tagname=='USER') echo "</tr>"; else if ($tagname=='NAME') echo "</td>"; else if ($tagname=='EMAIL') echo "</td>"; else if ($tagname=='STATUS') echo "</td>"; array_shift($this->attrstack); array_shift($this->tagstack); }
function parse($line,$end=1) // Добавить текст $end==1 - конец xml-кода { // Обработать строку файла XML // При возникновении ошибки прервать обработку // и вывести сообщение об ошибке. if ( ! xml_parse($this->xmlparser, $line, $end)) { die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($this->xmlparser)), xml_get_curren_line_number($this->xml_parser))); } }
}
// Открыть файл XML для обработки // Создать новый объект $xml_parser = new XMLHTML(); $xmlbuff = '';
function call($buff) // Функция сохраняет буфер в переменной { $GLOBALS['xmlbuff']=$buff; }
ob_start("call") ; // Пишем в буфер echo '<?xml version="1.0" ?>'."\n"; ?> <userlist> <user> <name>hRUndic</name> <email>[email protected]</email> <status>Администратор</status> </user> <user> <name>beowulf</name> <email>[email protected]</email> <status>Юзер</status> </user> <user> <name>Растафарай</name> <email>[email protected]</email> <status>Юзер</status> </user> </userlist> <? ob_end_flush(); $xml_parser->parse($xmlbuff); ?>
|
Как вы видите, все идет, без всяких расширений.
(см здесь)Впрочем, это не лучший пример в смысле быстродействия. Если данных будет много, то страница может забуксовать (пока выведешь xml-буфер, пока этот буфер весь оттранслируется в ХТМЛ). Поэтому рассмотрим ситуацию, когда данных может быть очень много. Будем выводить их по частям.
Код |
<META HTTP-EQUIV="Content-Type" content="text/html; charset=windows-1251 "> <? include "mydb.cfg"; // Для подключения к БД
class XMLHTML { VAR $xmlparser; VAR $tagcolor ="#800000"; VAR $datacolor ="#0000ff"; VAR $tagstack; // Стек тегов VAR $attrstack;// Стек наборов аттрибутов
function XMLHTML( ) { $this->xmlparser = xml_parser_create(); $this->tagstack = Array(); $this->attrstack = Array(); xml_set_object($this->xmlparser, &$this); xml_set_element_handler($this->xmlparser, "startTag", "endTag"); xml_set_character_data_handler($this->xmlparser, "characterData"); }
// Функция отвечает за обработку всех открывающих тегов.
function startTag($parser, $tagname, $attributes) { if ($tagname=='USERLIST') echo "<table border='7'>"; else if ($tagname=='USER') echo "<tr>"; else if ($tagname=='NAME') echo "<td>"; else if ($tagname=='EMAIL') echo "<td>"; else if ($tagname=='STATUS') echo "<td>"; array_unshift($this->tagstack,$tagname); array_unshift($this->attrstack,$attributes); }
// Функция отвечает за обработку всех символьных данных. function characterData($parser, $characterData) { GLOBAL $datacplor; if ($this->tagstack[0]=='EMAIL') echo "<a href=\"mailto:$characterData\">МЫЛИТЬ</a>"; else if ($this->tagstack[0]=='STATUS') { $stLabels["00000"]=array('capt'=>"Пользователь",'col'=>'blue'); $stLabels["00001"]=array('capt'=>"Администратор кладовки",'col'=>'red'); $stLabels["00002"]=array('capt'=>"Главный администратор",'col'=>'magenta'); $stLabels["00003"]=array('capt'=>"Запрещенный пользователь",'col'=>'black'); echo "<font color='".$stLabels[$characterData]['col']."'>".$stLabels[$characterData]['capt']."</font>"; } else if ($this->tagstack[0]=='NAME') echo strtolower($this->tagstack[0])."<b><font color='blue'>$characterData</font></b>"; //print "<font size=\"-2\" color=\"".$this->datacolor."\" face=\"arial, verdana\"> $characterData</font> <br>"; }
// Функция отвечает за обработку всех закрывающих тегов. function endTag($parser, $tagname) { GLOBAL $tagcolor; if ($tagname=='USERLIST') echo "</table>"; else if ($tagname=='USER') echo "</tr>"; else if ($tagname=='NAME') echo "</td>"; else if ($tagname=='EMAIL') echo "</td>"; else if ($tagname=='STATUS') echo "</td>"; array_shift($this->attrstack); array_shift($this->tagstack); }
function parse($line,$end=1) // Добавить текст $end==1 - конец xml-кода { // Обработать строку файла XML // При возникновении ошибки прервать обработку // и вывести сообщение об ошибке. if ( ! xml_parse($this->xmlparser, $line, $end)) { die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($this->xmlparser)), xml_get_current_line_number($this->xmlparser))); } }
}
// Открыть файл XML для обработки // Создать новый объект $xml_parser = new XMLHTML(); $xmlbuff = '';
function call($buff) { $GLOBALS['xmlbuff']=$buff; }
ob_start("call") ; echo '<?xml version="1.0" ?>'."\n"; ?> <userlist> <? ob_end_flush(); $xml_parser->parse($xmlbuff,0); $res=mysql_query("SELECT * FROM phpbb_users"); while($row=mysql_fetch_array($res)) { $xml_parser->parse("<user><name>".$row["username"]."</name>\n<email>".$row["user_email"]."</email>\n<status>".$row["ustatus"]."</status>\n</user>",0); //echo "<user><name>".$row["username"]."</name>\n<email>".$row["user_email"]."</email>\n<status>".$row["ustatus"]."</status>\n</user>"; } $xml_parser->parse("</userlist>"); ?>
|
Результат работы
здесьНу а теперь, несколько облагородим написанное нами. Сделаем отдельный файл презентации, где будут обработчики для каждого тега. Условимся наименовать функции следующим образом:
function <имя тега>_beg($аттрибуты) - обработчик начала тега
function <имя тега>_cont($содержимое внутри тега,$аттрибуты) - обработчик содержимого тега
function <имя тега>_end($аттрибуты) - обработчик конца тега
Файл pres1.pres будет выглядеть следующим образом:
Код |
<? // userlist
function userlist_beg($attrs) { echo "<table border='7'>"; }
function userlist_cont($content,$attrs) {
}
function userlist_end() { echo "</table>"; }
// user
function user_beg($attrs) { echo "<tr>"; }
function user_cont($content,$attrs) {
}
function user_end() { echo "</tr>"; }
// name function name_beg($attrs) { echo "<td>"; }
function name_cont($content,$attrs) { echo "<b>$content</b>"; }
function name_end() { echo "</td>"; }
// email function email_beg($attrs) { echo "<td>"; }
function email_cont($content,$attrs) { echo "<a href=\"mailto:$content\">МЫЛИТЬ</a>"; }
function email_end() { echo "</td>"; }
// status function status_beg($attrs) { echo "<td>"; }
function status_cont($content,$attrs) { $stLabels["00000"]=array('capt'=>"Пользователь",'col'=>'blue'); $stLabels["00001"]=array('capt'=>"Администратор кладовки",'col'=>'red'); $stLabels["00002"]=array('capt'=>"Главный администратор",'col'=>'magenta'); $stLabels["00003"]=array('capt'=>"Запрещенный пользователь",'col'=>'black'); echo "<font color='".$stLabels[$content]['col']."'>".$stLabels[$content]['capt']."</font>"; }
function status_end() { echo "</td>"; }
?>
|
А сам скрипт ПХП будет выглядеть так:
Код |
<META HTTP-EQUIV="Content-Type" content="text/html; charset=windows-1251 "> <? include "mydb.cfg"; // Для подключения к БД include "pres/pres1.pres"; // Презенташка
class XMLHTML { VAR $xmlparser; VAR $tagcolor ="#800000"; VAR $datacolor ="#0000ff"; VAR $tagstack; // Стек тегов VAR $attrstack;// Стек наборов аттрибутов
function XMLHTML( ) { $this->xmlparser = xml_parser_create(); $this->tagstack = Array(); $this->attrstack = Array(); xml_set_object($this->xmlparser, &$this); xml_set_element_handler($this->xmlparser, "startTag", "endTag"); xml_set_character_data_handler($this->xmlparser, "characterData"); }
// Функция отвечает за обработку всех открывающих тегов.
function startTag($parser, $tagname, $attributes) { eval(strtolower($tagname)."_beg(\$attributes);"); array_unshift($this->tagstack,$tagname); array_unshift($this->attrstack,$attributes); }
// Функция отвечает за обработку всех символьных данных. function characterData($parser, $characterData) { eval(strtolower($this->tagstack[0])."_cont(\$characterData,\$this->attrstack[0]);"); }
// Функция отвечает за обработку всех закрывающих тегов. function endTag($parser, $tagname) { eval(strtolower($tagname)."_end();"); array_shift($this->attrstack); array_shift($this->tagstack); }
function parse($line,$end=1) // Добавить текст $end==1 - конец xml-кода { // Обработать строку файла XML // При возникновении ошибки прервать обработку // и вывести сообщение об ошибке. if ( ! xml_parse($this->xmlparser, $line, $end)) { die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($this->xmlparser)), xml_get_current_line_number($this->xmlparser))); } }
}
// Открыть файл XML для обработки // Создать новый объект $xml_parser = new XMLHTML(); $xmlbuff = '';
function call($buff) { $GLOBALS['xmlbuff']=$buff; }
ob_start("call") ; echo '<?xml version="1.0" ?>'."\n"; ?> <userlist> <? ob_end_flush(); $xml_parser->parse($xmlbuff,0); $res=mysql_query("SELECT * FROM phpbb_users"); while($row=mysql_fetch_array($res)) { $xml_parser->parse("<user><name>".$row["username"]."</name>\n<email>".$row["user_email"]."</email>\n<status>".$row["ustatus"]."</status>\n</user>",0); //echo "<user><name>".$row["username"]."</name>\n<email>".$row["user_email"]."</email>\n<status>".$row["ustatus"]."</status>\n</user>"; } $xml_parser->parse("</userlist>"); ?>
|
Вот результат. Как вы видите, все весьма не сложно и не требует специальных расширений. К тому же обработка тегов в ПХП позволяет куда гибче осуществлять преобразования, чем XSL.
ЗЫ: Хотите усовершенствовать движок? Скажите, как определить есть ли в ПХП в текущий момент функция с заданным именем.
Кстати, не думайте, что построение веб-страниц - единственная польза от такого движка. Ведь xml является очень удобным средством хранения информации. Вполне возможно, что вам придется устроить капремонт сайта или переезд на другую хостинг площадку. В этом случае такой движок послужит службу для переноса данных из одной БД в другую. Достаточно лишь написать соответствующие обработчики теговых событий в презенташках. Это позволит юзерам вашего сайта не регистрироваться заново, если ваш ресурс сменит прописку. Не исключено, что с помощью сей технологии можно организовать слияние данных с нескольких ресурсов (но тут придется потрудиться).
Само собой разумеется, что такой подход полезен и для таких ставших стандартом технологий как RSS, SOAP и др.
Это сообщение отредактировал(а) beowulf - 27.6.2007, 14:42