"Block is Object" — это шаблонный энжайн, сочетающий простой синтаксис, гибкость и большое количество возможностей. Энжайн обеспечивает идеальное отделение оформления от исходного кода, язык абсолютно не содержит конструкций языков программирования. Энжайн способствует модульному программированию, так как распечатка каждого блока задаётся отдельным классом. Ниже приведён полный перечень возможностей:
foreach
, ни if
. Управляющие конструкции вынесены из шаблона в PHP-модуль. Таким образом, составлять шаблон может человек, ничего не знающий о программировании. Это основное отличие BO от PHP и Smarty;
<?bo ... ?>
(спецификация XML, п. 2.6 "Инструкции обработки");
Далее приводятся примеры. Результат работы шаблона генерируется в реальном времени на сайте.
Шаблон имеет вид:
<p>Hello, <? name ?>!</p>
Код на PHP:
<?php $sourceFn = $folderName."/_.bo"; $r = HTML_Template_BO_includeFile($sourceFn, "single-byte", $GLOBALS["tplParam"]["dir"], true); if ($r[0]) { $tpl = $r[1]; $tpl->printb(array( 'name' => "Compiled World" )); } else { ?>Error!<?php } $r = HTML_Template_BO_interpretFile($sourceFn, "single-byte"); if ($r[0]) { $tpl = $r[1]; $tpl->printb(array( 'name' => "Interpreted World" )); } else { ?>Error!<?php }
В результате:
Hello, Compiled World!
Hello, Interpreted World!
Скомпилированный шаблон можно увидеть здесь.
Шаблон имеет вид:
Цены снижены на <? percent ?>%!
Код на PHP:
<?php $tpl->printb(array( 'percent' => 20 )); ?>
В результате:
Скомпилированный шаблон можно увидеть здесь.
Шаблон имеет вид:
<table border="1"> <tr> <? block ( ?> <td><? name ?>, <? age ?> лет</td> <? ) ?> </tr> </table>
Код на PHP:
<?php $users = array ( array('name' => 'Иван', 'age' => 15) , array('name' => 'Василий', 'age' => 18) , array('name' => 'Николай', 'age' => 17) ); $tpl->printb(array( 'block' => new HTML_Template_BO_RArray($users) )); ?>
В результате:
Иван, 15 лет | Василий, 18 лет | Николай, 17 лет |
Скомпилированный шаблон можно увидеть здесь.
Шаблон имеет вид:
<p> Окончание акции 12.09.2006<? edit ( ?> <a href="<? url ?>">отредактировать</a>, если вы администратор<? ) ?>. </p>
Код на PHP:
<?php $tpl->printb(array( 'edit' => new HTML_Template_BO_RMaybeArg(true, array("url" => "#1")) )); $tpl->printb(array( 'edit' => new HTML_Template_BO_RMaybeArg(false) )); ?>
В результате:
Окончание акции 12.09.2006 отредактировать, если вы администратор.
Окончание акции 12.09.2006.
Скомпилированный шаблон можно увидеть здесь.
Если при печати строки базы данных необходимо менять её оформление в зависимости от типа записи.
Шаблон имеет вид:
<? users { banned ?> <p style="color: red"><? name ?>, заблокированный. </p> <? , moderator ?> <p style="font-weight: bold;"><? name ?>, модератор, <? posts ?> сообщений.</p> <? , ordinal ?> <p><? name ?>, пользователь, <? posts ?> сообщений. </p> <? } ?>
Код на PHP:
<?php $users = array ( array("ordinal", array("name" => "Иван", "posts" => 34)) , array("moderator", array("name" => "Василий", "posts" => 125)) , array("banned", array("name" => "Николай", "posts" => 2)) , array("ordinal", array("name" => "Игорь", "posts" => 54)) , array("ordinal", array("name" => "Дмитрий", "posts" => 4)) , array("banned", array("name" => "Сергей", "posts" => 20)) ); $tpl->printb(array ( "users" => new HTML_Template_BO_RArraySwitch($users) )); ?>
В результате:
Иван, пользователь, 34 сообщений.
Василий, модератор, 125 сообщений.
Николай, заблокированный.
Игорь, пользователь, 54 сообщений.
Дмитрий, пользователь, 4 сообщений.
Сергей, заблокированный.
Скомпилированный шаблон можно увидеть здесь.
Можно создавать библиотеки шаблонов, содержащие фрагменты html-кода. Эти фрагменты можно затем вставлять в другие шаблоны. Вставку следует делать программно, т. е. фрагмент html надо передать шаблону как одну из переменных. Специальной инструкции include
в шаблоне нет.
Шаблон-библиотека должна иметь расширение .lib.bo
.
<? { button ?> <a href="http://www.yandex.ru/cy?base=0&host=beroal.progz.org"> <img src="http://www.yandex.ru/cycounter?beroal.progz.org" width="88" height="31" alt="Яндекс цитирования" border="0"> </a> <? , button2 ?> ... <? }
Другой шаблон, который использует библиотеку:
Это денежка Яндекса: <? button ?>.
Код на PHP:
<?php $lib = include1($folderName.'/_.lib.bo'); $tpl->printb(array ( 'button' => $lib['button'] )); ?>
В результате:
Данные в шаблоне можно конвертировать с помощью специальных объектов-конвертеров. Конвертеры являются классами PHP, у которых определён метод convert
. Конвертеры должны быть наследованы от HTML_Template_BO_Converter
. Можно конвертировать как значения переменных, так и целые HTML-блоки. Можно применить несколько конвертеров последовательно.
Шаблон имеет вид:
<p>(<? upper ( ?>привет<? ) ?>) (<? dotted ( ?>привет<? ) ?>) (<? dotted upper ( ?>привет<? ) ?>)</p> <p>(<? upper name ?>) (<? dotted name ?>) (<? dotted upper name ?>)</p> <p>(<? upper "литерал" ?>) (<? dotted "литерал" ?>) (<? dotted upper "литерал" ?>)</p>
Код на PHP:
<?php class UpperConv extends HTML_Template_BO_Converter { function convert($x) { return strtoupper(HTML_Template_BO_printToString($x)); } } class DottedConv extends HTML_Template_BO_Converter { function convert($x) { $s = HTML_Template_BO_printToString($x); $s1 = ''; for($i = 0; $i < strlen($s); $i++) { $s1 .= $s[$i].'.'; } return $s1; } } $tpl->printb(array ( 'upper' => new UpperConv() , 'dotted' => new DottedConv() , 'name' => "Дмитрий" )); ?>
В результате:
(привет) (п.р.и.в.е.т.) (п.р.и.в.е.т.)
(Дмитрий) (Д.м.и.т.р.и.й.) (Д.м.и.т.р.и.й.)
(литерал) (л.и.т.е.р.а.л.) (л.и.т.е.р.а.л.)
Скомпилированный шаблон можно увидеть здесь.
Используя конвертер, можно вынести блок в отдельный файл.
Вот файл external.bo
с содержимым блока:
Hi, <? name ?>!
Шаблон:
<p> <? hello admin ?> </p>
Код на PHP:
<?php $tpl->printb(array ( "hello" => new HTML_Template_BO_RMaybeArg(true , array("name" => "Administrator")) , "admin" => include1($folderName."/external.bo") )); ?>
В результате:
Hi, Administrator!
Если количество переменных большое, их можно организовать в дерево, используя массивы со строковыми ключами. Вложенность массивов произвольная. Шаблон имеет встроенный механизм для обращения к дереву. Имя переменной представляет собой путь в этом дереве. Отдельные папки разделяются точками.
Шаблон имеет вид:
<p>Данные о товаре: <br/> <strong><? caption.name ?>:</strong> <? ware.name ?> <br/> <strong><? caption.price ?>:</strong> <? ware.price ?> <br/> <strong><? caption.quantity ?>:</strong> <? ware.quantity ?> </p>
Код на PHP:
<?php $variableA = array ( "caption" => array ( "name" => "Название товара" , "price" => "Цена, руб." , "quantity" => "Количество на складе, шт." ) , "ware" => db_query("select * from wares where id=5") ); $tpl->printb($variableA); ?>
В результате:
Данные о товаре:
Название товара: ноутбук Acer 3003LC
Цена, руб.: 20000
Количество на складе, шт.: 1
Скомпилированный шаблон можно увидеть здесь.
Допустим, есть ситуцация, когда два блока, вложенные друг в друга, вводят переменные с одинаковыми именами. В этом случае переменная внутреннего блока заменяет переменную внешнего блока. Если вы хотите обратиться к переменной внешнего блока, ей необходимо присвоить отдельное пространство имён. Присвоение пространства имён происходит полностью в шаблона и не требует доработки программного кода, поэтому программист может не беспокоиться о совпадающих именах переменных.
Шаблон имеет вид:
<p>Новости</p> <? news ( # category ?> <p><strong><? category:name ?></strong> <a href="#">news_category.php?oper=edit&id=<? category:id ?></a> <ul> <? items ( # item ?> <li><? item:name ?> <a href="#">news_item.php?oper=edit&id=<? item:id ?></a></li> <? ) ?> </ul> </p> <? ) ?>
Код на PHP:
<?php $news = new HTML_Template_BO_RArray(array ( array ( "id" => 1 , "name" => "Политика" , "items" => new HTML_Template_BO_RArray(array ( array ( "id" => 1 , "name" => "Аааа" ) , array ( "id" => 2 , "name" => "Ббббббб" ) )) ) , array ( "id" => 2 , "name" => "Финансы" , "items" => new HTML_Template_BO_RArray(array ( array ( "id" => 3 , "name" => "Ввввввв" ) , array ( "id" => 4 , "name" => "Гггггг" ) )) ) )); $tpl->printb(array( "news" => $news )); ?>
В результате:
Новости
Политика news_category.php?oper=edit&id=1
Финансы news_category.php?oper=edit&id=2
Скомпилированный шаблон можно увидеть здесь.
Можно вставлять C-шные комментарии /* ... */
.
Шаблон имеет вид:
<table border="1"> <tr> <?/*comment*/block/*comment*/(/*comment*/?> <td><?/*comment*/name/*comment*/?>, <?age?> лет</td> <?)?> </tr> </table>
Код на PHP:
<?php $users = array ( array('name' => 'Иван', 'age' => 15) , array('name' => 'Василий', 'age' => 18) , array('name' => 'Николай', 'age' => 17) ); $tpl->printb(array( 'block' => new HTML_Template_BO_RArray($users) )); ?>
В результате:
Иван, 15 лет | Василий, 18 лет | Николай, 17 лет |
Скомпилированный шаблон можно увидеть здесь.
Шаблон имеет вид:
<style> .a, .b { border-width: 1px; border-color: black; border-top-style: none; border-right-style: none; border-left-style: solid; margin-left: 10px; } .a { border-bottom-style: solid; } .b, .c { height: 100%; } .b { border-bottom-style: none; } </style> <div>root</div> <? recursive ( ?> <table border="0" cellspacing="0" cellpadding="0"> <tr> <td style="width: 20px;"> <div class="a" ><span style="font-size: 65%"> </span></div> <div class="<? last { false ?>b<? , true ?>c<? } ?>"><img src="spacer.gif" /></div> </td> <td><div><? name ?></div><? child ?></td> </tr> </table> <? ) ?>
Код на PHP:
<?php class RRecursivePa { var $block; var $data; function RRecursivePa($block, $data) { $this->block = $block; $this->data = $data; } function print_() { $a = array(); $last = count($this->data); foreach ($this->data as $name => $data1) { $last--; $this->block->printb(array ( "name" => $name , "child" => new RRecursivePa($this->block, $data1) , "last" => new HTML_Template_BO_RBool(0==$last) )); } } } class RRecursive { var $data; function RRecursive($data) { $this->data = $data; } function printr($block) { $pa = new RRecursivePa($block, $this->data); $pa->print_(); } } $data = array ( "aaaaa" => array ( "bbbbb" => array() , "cc" => array ( "www" => array() , "hhhhhhh" => array() ) ) , "dddd" => array() , "deee" => array ( "sssssss" => array ( "www" => array() , "llll" => array ( "xxxxxxx" => array ( "yyyyyyy" => array ( "zzzzzzz" => array() ) ) ) , "mmmmmmm" => array() , "nnnnnn" => array() ) ) ); $tpl->printb(array( "recursive" => new RRecursive($data) )); ?>
В результате:
![]() |
aaaaa
|
![]() |
dddd |
![]() |
deee
|
Скомпилированный шаблон можно увидеть здесь.
Если шаблон простой, то есть он содержит только переменные, он работает так. Блок в шаблоне преобразуется компилятором в объект, имеющий метод printb
. Чтобы напечатать блок, мы вызываем метод printb
, передавая ему массив со значениями переменных, которые имеются в блоке. Пример.
<? block ( ?>x: <? x ?>; y: <? y ?>; z: <? z ?>;<? ) ?>
$block->printb(array("x" => 1, "y" => 2, "z" => 3));
Что выполняется, если внутри блока находится ещё один блок. В массив переменных добавляется специальный объект, называемый Renderer
-ом. При печати блока (того, который внутри другого блока), у Renderer
-а вызывается метод printr
, которому передаётся блок как объект с методом printb
. Пример.
<? block ( ?>x: <? x ?>; внутренний блок: <? inner ( ?>...<? ) ?>; z: <? z ?>;<? ) ?>
class R { function printr($block) { $block->printb(array()); } } $block->printb(array("x" => 1, "inner" => new R(), "z" => 3));
Есть несколько готовых Renderer
-ов, выполняющих типичные операции: разворачивание массива, печать одного из нескольких вариантов текста, текст, который может быть видимым или невидимым.
Ниже приведен исходный код стандартных Renderer
-ов.
<?php function HTML_Template_BO_printToString($p) { flush(); ob_start(); HTML_Template_BO_print($p); $ret= ob_get_contents(); ob_end_clean(); return $ret; } class HTML_Template_BO_RSwitch extends HTML_Template_BO_Renderer { var $variableA; var $key; function HTML_Template_BO_RSwitch($key, $variableA = array()) { $this->variableA = $variableA; $this->key = $key; } function printr($assoc) { if (isset($assoc[$this->key])) { $block= $assoc[$this->key]; $block->printb($this->variableA); } } } class HTML_Template_BO_RBool extends HTML_Template_BO_RSwitch { function HTML_Template_BO_RBool($key, $variableA = array()) { parent::HTML_Template_BO_RSwitch($key ? 'true' : 'false' , $variableA); } } class HTML_Template_BO_RArray extends HTML_Template_BO_Renderer { var $list; function HTML_Template_BO_RArray($list) { $this->list = $list; } function printr($block) { foreach($this->list as $variableA) { $block->printb($variableA); } } } class HTML_Template_BO_RArraySwitch extends HTML_Template_BO_Renderer { var $list; function HTML_Template_BO_RArraySwitch($list) { $this->a = $list; } function printr($assoc) { foreach($this->a as $x) { list($key, $variableA) = $x; $assoc[$key]->printb($variableA); } } } class HTML_Template_BO_RArrayR extends HTML_Template_BO_Renderer { var $rendererA; function HTML_Template_BO_RArrayR($rendererA) { $this->rendererA = $rendererA; } function printr($x) { foreach($this->rendererA as $renderer) { $renderer->printr($x); } } } class HTML_Template_BO_TplToPrintable extends HTML_Template_BO_Printable { var $variableA; var $block; function HTML_Template_BO_TplToPrintable($block, $variableA) { $this->variableA = $variableA; $this->block = $block; } function print_() { $this->block->printb($this->variableA); } } class HTML_Template_BO_RBlock extends HTML_Template_BO_Renderer { var $variableA; function HTML_Template_BO_RBlock($variableA) { $this->variableA = $variableA; } function printr($block) { $block->printb($this->variableA); } } class HTML_Template_BO_RMaybe extends HTML_Template_BO_Renderer { var $maybe; function HTML_Template_BO_RMaybe($maybe) { $this->maybe = $maybe; } function printr($block) { if ($this->maybe[0]) { $block->printb($this->maybe[1]); } } } class HTML_Template_BO_RMaybeArg extends HTML_Template_BO_RMaybe { function HTML_Template_BO_RMaybeArg($cond, $variableA = array()) { parent::HTML_Template_BO_RMaybe(array($cond, $variableA)); } }
open_tag ::= '<?' (| 'bo') close_tag ::= '?>' resursive ::= name namespace ::= name variable ::= name assoc_index ::= name unidata ::= number | string | list | assoc | converter data ::= block | unidata top_data ::= single | unidata single ::= '(' block ')' list ::= '[' (|data (',' data)*) ']' assoc ::= '{' (|assoc_index data (',' assoc_index data)*) '}' substitute ::= (|namespace ':') variable (| top_data) converter ::= substitute printer ::= substitute block_text ::= text (open_tag printer close_tag text)* block ::= (|'@' resursive) (|'#' namespace) close_tag block_text open_tag template ::= block_text | (open_tag top_data close_tag)
name
— имя, содержит буквы, цифры и подчёркивание и должно начинаться с буквы или подчёркивания (регулярное выражение для имени: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\.]*
).
© 2006, Роман Беслик (форма обратной связи).