Главная | Контакты | Настройки СМЕНИТЬ ПАЛИТРУ:

Главная > Программирование > 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.

Материал взят с сайта: http://www.codenet.ru/webmast/php/Templater.php

Главная > Программирование > PHP