Главная > Программирование > PHP Шаблонизатор на PHP своими рукамиЭта статья предназначена для тех кто не относится скептически к написанию того что уже написано, в общем она для энтузиастов, каким я сам и являюсь. Начнем с того, что определим что будет уметь наш шаблонизатор:
Блоки.Блоки могут быть "циклические" и "не циклические". К первым отнесем блоки приразборе которых шаблонизатор выполнит цикл с определенными параметрами и вставит "внутренности" блока в результирующую страницу несколько раз. Они нам понадобятся массовой для публикации данных (например список заголовков новостей). Ко вторым отнесем условные конструкции (зачастую без них не обойтись). Имена блоков будем хранить в массиве: $Blocks=array (array('открывающий тег',
'закрывающий тег',признак цикличного блока));
Чтобы было понятнее разберем пример. есть таблица новостей: create table news
(
id int(10) unsigned NOT NULL auto_increment,
title varchar(128),
body text,
ndate date,
primary key (id)
);
нужно создать шаблон для вывода этих новостей и php скрипт обрабатывающий этот шаблон. Договоримся - теги блоков и переменных будем заключать в HTML комментарии: <!--news-->
содержимое блока
<!--end_news-->
теперь функция выделения блока или переменной: function ParseTmpl($Block)
{
global $Blocks;
global $Vars;
// Ищем блоки
$Pos=0;
while (($Pos=strpos($Block,'<!--',$Pos))!==false)
{
$BlockId=GetIsBlock($Block,$Pos);
if ($BlockId!=-1)
{
// ищем закрывающий тег блока
$BlockEnd='<!--'.($Blocks[$BlockId][1]).'-->';
if ($EndIndex=strpos($Block,$BlockEnd,$Pos))
{
$EndIndex+=strlen($BlockEnd);
$SubBlock=substr($Block,$Pos,$EndIndex-$Pos);
$Block=substr($Block,0,$Pos).
Block($SubBlock,$BlockId).
substr($Block,$EndIndex);
}
}
else
{
$Pos=strpos($Block,'-->',$Pos);
}
}
//Выделяем переменные
for ($i=0; $i<count($Vars); $i++)
{
$Begin=$Vars[$i];
while ($Pos=strpos($Block,'<!--'.$Begin))
{
$BeginIndex=$Pos;
if ($EndIndex=strpos($Block,'-->',$BeginIndex))
{
$Sub=substr($Block,$BeginIndex,$EndIndex-$BeginIndex+3);
$Block=substr($Block,0,$BeginIndex).
Variable($Sub,$i).substr($Block,$EndIndex+3);
}
}
}
return $Block;
}
В качестве аргумента ей передается шаблон или его фрагмент (почему фрагмент? Потому что в нутри блока может быть еще один блок, то есть "внутренность" блока необходимо обработать отдельно). Далее она ищет вхождения строки <!--, если находит, то проверяет не является ли найденная строка тегом шаблонизатора, для этого напишем еще одну функцию - GetIsBlock($Block,$Offset), которой передаются тот же фрагмент шаблона и смещение в нутри его, то есть позицию найденного вхождения стоки <!--, возвращает она индекс имени блока из массива $Blocks или -1 если найденная строка не является тегом шаблонизатора.Код этой функции можно увидеть немного ниже. продолжаем разбирать шаблон. Если после проверки выясняется, что найденная строка - тег шаблонизатора, и является блочным, ищем его закрывающий тег. Выделяем подблок, то есть сами теги блока и внутренности, и передаем все это функции Block($Block, $BlockId), которая обрабатывает блок. Теперь обещанная функция GetIsBlock($Block,$Offset), она простая поэтому думаю комментировать нет необходимости: function GetIsBlock($Block,$Offset)
{
global $Blocks;
// Выделяем имя блока
$EndInd=strpos($Block,'-->',$Offset);
if (($Offset+4)!=$EndInd)
{
$S=substr($Block,$Offset+4,$EndInd-($Offset+4));
$Bl=explode(' ',$S);
$BlockName=$Bl[0];
for ($i=0; $i<count($Blocks); $i++)
{
if ($Blocks[$i][0]==$BlockName)
{
return $i;
}
}
}
return -1;
}
Рассмотри работу функции Block($Block, $BlockId): function Block($Block, $BlockId)
{
global $Blocks;
$Ret='';
// Получаем параметры
$BeginStr=$Blocks[$BlockId][0];
$BeginLength=4+strlen($BeginStr);
$EndOperatorPos=strpos($Block,'-->');
$Operator=substr($Block,$BeginLength,$EndOperatorPos-$BeginLength);
$Args=explode(' ',trim($Operator));
$EndBlockPos=strpos($Block,'<!--'.$Blocks[$BlockId][1].'-->');
$SubBlock=substr($Block,$EndOperatorPos+3,$EndBlockPos-$EndOperatorPos-3);
if ($Blocks[$BlockId][2]) // блок не цикличный
{
if ($BeginStr($Args))
{
$Ret.=ParseTmpl($SubBlock);
}
}
else
{
while ($BeginStr($Args))
{
$Ret.=ParseTmpl($SubBlock);
}
}
return $Ret;
}
Ей передаются фрагмент шаблона, содержащий рассматриваемый блок, и индекс блока в массиве $Blocks. Для начала в ней мы разбираем строку открывающего тега, на предмет параметров (это на всякий случай, а вдруг Вам необходимо вывести не все новости а какое-то количество). То есть если блок новостей начинается со строки: <!--news 5-->
Где 5 - количество выводимых новостей. Мы должны иметь возможность выделить именно определенное количество новостей. Далее выделяется "внутренность" блока в переменню $SubBlock, для дальнейшего парсирования. Далее в зависимости от результатов проверки на цикличность блока вызываем уже знакомую нам функцию ParseTmpl, либо при выполнении условия, либо в цикле до не выполнения условия. Условием является не нулевой результат выполнения функции одноименной с блоком. Для каждого циклического блока необходимо создать функцию которая бы возвращала 0 или не 0 в зависимости от того нужно ли повторять разбор блока. Это может быть функция которая будет при каждом вызове выбирать очередную порцию данных из таблицы новостей и возвращать 1 если например количество выбранных новостей не больше количества вереданного ей в качестве одного из элементов массива аргументов блока. Переменные.Переменные будем хранить в массиве $Vars=array('имя переменной');
Так же как и в случае с блоками создадим одноименные функции которые бы возвращали значения этих переменных (брали их, например, из базы данных). Вот участок функции ParseTmpl выделяющий переменные из шаблона и подставляющий их значения: for ($i=0; $i<count($Vars); $i++)
{
$Begin=$Vars[$i];
while ($Pos=strpos($Block,'<!--'.$Begin))
{
$BeginIndex=$Pos;
if ($EndIndex=strpos($Block,'-->',$BeginIndex))
{
$Sub=substr($Block,$BeginIndex,$EndIndex-$BeginIndex+3);
$Block=substr($Block,0,$BeginIndex).
Variable($Sub,$i).substr($Block,$EndIndex+3);
}
}
}
И функция Variable: function Variable($Block, $VarId)
{
global $Vars;
$Ret='';
// Получаем параметры
$BeginStr=$Vars[$VarId];
$BeginLength=4+strlen($BeginStr);
$EndOperatorPos=strpos($Block,'-->');
$Operator=substr($Block,$BeginLength,$EndOperatorPos-$BeginLength);
$Args=explode(' ',trim($Operator));
return $BeginStr($Args);
}
Она похожа на функцию Block, но значительно проще. Пример шаблона и функции блоков и переменных. Шаблон вывода наших новостей: <table>
<!--news 5-->
<tr>
<td>
[<b><!--ndate--></b>] <!--ntitle-->
</td>
</tr>
<tr>
<td>
<!--nbody-->
</td>
</tr>
<!--end_news-->
</table>
А вот код для работы с блоками и переменными: $QueryResult_News=0;
$QueryRow_News=0;
function news($Args) // Блок news
{
global $QueryResult_News;
global $QueryRow_News;
$Active=$Args[0];
if ($QueryResult_News==0)
{
$QueryResult_News=mysql_query("select *
from news
by ndate desc limit $Active");
}
if ($QueryRow_News=mysql_fetch_array($QueryResult_News))
{
return 1;
}
else
{
$QueryResult_News=0;
return 0;
}
}
function ndate($Args) // переменная ndate
{
global $QueryRow_News;
if (isset($QueryRow_News['ndate']))
{
return implode('.',array_reverse(explode('-',$QueryRow_News['ndate'],3)));
}
else
{
return '';
}
}
function ntitle($Args) // переменная ntitle
{
global $QueryRow_News;
if (isset($QueryRow_News['title']))
{
return $QueryRow_News['title'];
}
else
{
return '';
}
}
function nbody($Args) // переменная nbody
{
global $QueryRow_News;
if (isset($QueryRow_News['body']))
{
return $QueryRow_News['body'];
}
else
{
return '';
}
}
Массивы $Blocks и $Vars: $Blocks=array(array('news','news',0))
$Vars=array('ndate','ntitle','nbody');
Ну вот и все простой шаблонизатор готов, все что я хотел сделал, Вам же теперь его модернизировать и усовершенствовать до тех пор пока он не станет удовлетворять всем Вашим ожиданиям :) Удачи, искренне Ваш elrevin. Материал взят с сайта:Главная > Программирование > PHP |