Модераторы: Aliance, skyboy, MoLeX, ksnk
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> xml-движок на PHP стандартными средствами 
:(
    Опции темы
beowulf
Дата 23.6.2007, 11:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 56
Регистрация: 20.3.2006

Репутация: нет
Всего: 1



Всякий программист, как известно всегда стремится к отделению формы от содержания - внешнего вида от данных. В 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\">&nbsp; &nbsp; &nbsp; $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\">&nbsp; &nbsp; &nbsp; $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
PM MAIL WWW YIM   Вверх
Golda
Дата 12.7.2007, 18:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 460
Регистрация: 26.3.2007
Где: Ариель, Израиль

Репутация: 5
Всего: 42



Неплохо. Но тут Вы завязываны на другой extension - expat (правда под Windows ставить дополнительно не надо, но в Unix/Linux проблема остается). Поставить expat - не проблема, а sablotron - ни-ни  smile 


--------------------
"For every problem, there exists a simple and elegant solution which is absolutely wrong." -- J. Wagoner, U.C.B. Mathematics
PM MAIL   Вверх
Diesel Draft
Дата 7.8.2007, 12:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Статья гуд. Но пример плохой. Это очень не универсально.  Но давая статья заставила задуматься над другим, больше универсальным. Способов


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: X технологии | Следующая тема »


 




[ Время генерации скрипта: 0.1253 ]   [ Использовано запросов: 23 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.