Вот решил написать шаблонизатор "под себя", опыт роботы с пхп у меня небольшой потому будет интересно услышать ваши мнения и замечания, я работал с шаблонизатором Smarty и пришел к выводу что иногда надо что-то облегченное с реализацией основных функций, вот и написал такой клас... Тема много раз затрагивалась но хотел бы получить совет как лучше реализовать компиляцию и сохранение в кеш... Да и необходимо ли. Клас class.template.php: PHP: <?php define("TPL_TAG", "tpl"); /*** how to define a variable ***********************/ /*** <tpl_var name="%variable name%"> ***************/ /*** how to define a loop ***************************/ /*** <tpl_loop name="%loop name%" *******************/ /*** <tpl_loop_var name="%loop variable name%"> *****/ /*** <tpl_loop_var> *********************************/ /*** <tpl_loop_counter> *****************************/ /*** </tpl_loop> ************************************/ /*** how to define an if-else construction **********/ /*** <tpl_if %variable%="%value%"> ******************/ /*** <tpl_else> *************************************/ /*** </tpl_if> **************************************/ /*** how to include a file **************************/ /*** <tpl_file name="%filename%"> *******************/ class template { private $data, $vars; private $tag_types; public $path="./tpl"; public function __construct($tpl_file) { if(!file_exists($this->path."/".$tpl_file)) die("ERROR: MAIN TEMPLATE \"$this->path/$tpl_file\" NOT FOUND"); $this->data = @file_get_contents($this->path."/".$tpl_file); if(!$this->data) die("ERROR: FILE_GET_CONTENTS \"$this->path/$tpl_file\""); $this->data=trim($this->data); $this->tag_types=array("var", "loop", "if"); $this->vars = array(); } public function assign($name, $value) { if($name == '') die("ERROR: ASSIGN NAME=''"); $this->vars[$name]=$value; } public function parse() { while($tpl_tag=strpos($this->data, "<".TPL_TAG."_")) { $pos1=strpos($this->data, "_", $tpl_tag); $pos2=strpos($this->data, " ", ++$pos1); if(!$pos1 || !$pos2) die("ERROR: WRONG TPL TAG"); $tpl_type=substr($this->data, $pos1, $pos2 - $pos1); if(!in_array($tpl_type, $this->tag_types)) die("ERROR: WRONG TYPE TPL TAG"); $pos1=$pos2; $pos2=strpos($this->data, "=\"", ++$pos1); if(!$pos2) die("ERROR: WRONG PARAM NAME IN TPL TAG"); $tpl_name=substr($this->data, $pos1, $pos2-$pos1); $pos1=++$pos2; $pos2=strpos($this->data, "\">", ++$pos1); if(!$pos2) die("ERROR: WRONG PARAM VALUE IN TPL TAG"); $tpl_value=substr($this->data, $pos1, $pos2-$pos1); switch($tpl_type) { case "var": if(isset($this->vars[$tpl_value])) { $this->data=str_replace("<".TPL_TAG."_var ".$tpl_name."=\"".$tpl_value."\">", $this->vars[$tpl_value], $this->data); } else { $tplstr="<".TPL_TAG."_var ".$tpl_name."=\"".$tpl_value."\">"; $this->data=str_replace(array($tplstr.chr(13).chr(10), $tplstr), "", $this->data); } break; case "loop": $pos1=strpos($this->data, "</".TPL_TAG."_loop>", $pos2)+strlen("</".TPL_TAG."_loop>"); $pos2=$tpl_tag; $loop_block=substr($this->data, $pos2, $pos1-$pos2); if(isset($this->vars[$tpl_value])) { $this->data=str_replace($loop_block, $this->process_loop($loop_block, $this->vars[$tpl_value]), $this->data); } else { $this->data=str_replace(array($loop_block.chr(13).chr(10), $loop_block), "", $this->data); } break; case "if": $pos1=strpos($this->data, "</".TPL_TAG."_if>", $pos2)+strlen("</".TPL_TAG."_if>"); $pos2=$tpl_tag; $if_block=substr($this->data, $pos2, $pos1-$pos2); if(isset($this->vars[$tpl_name])) { $this->data=str_replace($if_block, $this->process_if($if_block, array($tpl_name=>$this->vars[$tpl_name])), $this->data); } else { $this->data=str_replace(array($if_block.chr(13).chr(10), $if_block), "", $this->data); } break; default: break; } } } private function process_loop($loop_block, $data) { $pos1=strpos($loop_block, ">"); $pos1++; $pos2=strpos($loop_block, "</".TPL_TAG."_loop>"); $tpl=substr($loop_block, $pos1, $pos2-$pos1); $r=""; foreach($data as $i=>$item) { if(is_array($item)) { $temp=$tpl; $temp=str_replace("<".TPL_TAG."_loop_counter>", $i+1, $temp); foreach($item as $k=>$v) { $temp=str_replace("<".TPL_TAG."_loop_var name=\"$k\">", $v, $temp); } $r.=trim($temp); } else { $temp=$tpl; $temp=str_replace("<".TPL_TAG."_loop_counter>", $i+1, $temp); $r.=trim(str_replace("<".TPL_TAG."_loop_var>", $item, $temp)); } } return trim($r); } private function process_if($if_block, $data) { if(strstr($if_block, key($data)."=\"".current($data)."\"")) { $pos1=strpos($if_block, ">", 0); $pos1++; $pos2=(strstr($if_block, "<".TPL_TAG."_else>")) ? strpos($if_block, "<".TPL_TAG."_else>", $pos1) : strpos($if_block, "</".TPL_TAG."_if>", $pos1); $r=trim(substr($if_block, $pos1, $pos2-$pos1)); } else if(strstr($if_block, "<".TPL_TAG."_else>")) { $pos1=strpos($if_block, "<".TPL_TAG."_else>")+strlen("<".TPL_TAG."_else>"); $pos2=strpos($if_block, "</".TPL_TAG."_if>", $pos1); $r=trim(substr($if_block, $pos1, $pos2-$pos1)); } return $r; } public function inc_files() { while($inc_file_pos = strpos($this->data, "<".TPL_TAG."_file name=\"")) { $pos1=$inc_file_pos+strlen("<".TPL_TAG."_file name=\""); $pos2=strpos($this->data, "\"", $pos1); if(!$pos2) die("ERROR: WRONG TAG INC_FILES()"); $filename=substr($this->data, $pos1, $pos2-$pos1); if(!file_exists($this->path."/".$filename)) die("ERROR: INC TEMPLATE \"$this->path/$filename\" NOT FOUND"); $inc_data=@file_get_contents($this->path."/".$filename); if(!$inc_data) die("ERROR: INC FILE_GET_CONTENTS \"$this->path/$tpl_file\""); $inc_data=trim($inc_data); $this->data=str_replace("<".TPL_TAG."_file name=\"$filename\">", $inc_data, $this->data); } } public function print_html() { echo $this->data; } public function data() { return $this->data; } } ?> Пример шаблонов - основного и например инклуда в основной: file.tpl HTML: <html> <head> <title></title> </head> <body> <h2> <tpl_var name="title"> </h2> <tpl_var name="body"><br> <br><i>users & ids</i> :<br> <tpl_loop name="user_id"> <tpl_loop_counter> <tpl_loop_var name="user"> :: <tpl_loop_var name="id"><br> </tpl_loop> <tpl_if is_error="true"> <div style="border: 1px solid red;padding: 20px;width: 50px;height: 40px;"><b>IS</b> ERROR</div> <tpl_else> <div style="border: 1px solid blue;padding: 20px;width: 50px;height: 40px;"><b>NO</b> ERRORS</div> </tpl_if> <tpl_file name="inc.tpl"> </body> </html> inc.tpl HTML: <table> <tr> <td><i>inc_var1</i> =</td><td><tpl_var name="inc_var1"></td> </tr> <tr> <td><i>inc_var2</i> =</td><td><tpl_var name="inc_var2"></td> </tr> </table> Пример работы: PHP: <?php include "class.template.php"; $tpl = new template("file.tpl"); $tpl->assign("title", "Hello, World!"); $tpl->assign("body", "text in the block 'body'"); $tpl->assign("is_error", "false"); $tpl->assign("user_id", array(array("user"=>"user19", "id"=>"112"), array("user"=>"user3", "id"=>"22"), array("user"=>"user71", "id"=>"32"))); $tpl->assign("inc_var1", "inc_var_value1"); $tpl->assign("inc_var2", "inc_var_value2"); $tpl->inc_files(); $tpl->parse(); $tpl->print_html(); #echo $tpl->data(); $tpl=NULL; ?>
Код не очень внимательно посмотрел, может, посмотрю позже, но, по-моему, присутствует избыточность: HTML: <tpl_var name="inc_var2"> даже на нейтив-шаблонизаторе выглядит красивше: PHP: <?=$inc_var2?> Плюс, подсветка не очень корректно будет работать, да и ИДЕ с ума сходить будет: HTML: <a href="<tpl_var name="aHref">"><tpl_var name="aTitle"></a> Плюс — сливается все в один цвет при подсветке, так как, что синтаксис шаблонизатора, что синтаксис шаблонизирумого языка — одинаков. Мне более нравится идея совмещения <!-- BLOCK --> и {VAR} — синтаксис подсветился серым (так,как основа синтаксиса — хтмл-комментарии, которые практически не используются при верстке), а все переменные не разрушают структуру кода (иде нормально к ним относятся, так как воспринимают, как обычный текст), и очень легко печатать. Сравни: Код (Text): <tpl_var name="inc_var2"> {inc_var2} Но изобретение своих велосипедов — дело благородное и правильное.
Использование знаков < > в шаблоне это не правильно. Эти штуки зарезервированы для HTML, если говорить вообщем. Поэтому, лучше подобрать другие признаки.
HTML: [:loop myArray:] <tr> <td>[:id:]</td><td>[:title:]</td> </tr> [:/loop:] а ваще - пользователь шаблонизатора сам вправе выбирать какие именно символы использовать {} [] {%%} [::] ...
согласен. в настройках шаблона указать: PHP: <? $var = '{*}'; $block = '((*))'; где звездочка — сам код.