%PDF- %PDF-
| Direktori : /www/varak.net/losik.varak.net/vendor/latte/latte/src/Latte/Macros/ |
| Current File : /www/varak.net/losik.varak.net/vendor/latte/latte/src/Latte/Macros/CoreMacros.php |
<?php
/**
* This file is part of the Latte (https://latte.nette.org)
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Latte\Macros;
use Latte;
use Latte\CompileException;
use Latte\Engine;
use Latte\Helpers;
use Latte\MacroNode;
use Latte\PhpHelpers;
use Latte\PhpWriter;
/**
* Basic macros for Latte.
*/
class CoreMacros extends MacroSet
{
/** @var array<string, int[]> */
private $overwrittenVars;
/** @var string|null */
private $printTemplate;
/** @var int */
private $idCounter = 0;
public static function install(Latte\Compiler $compiler): void
{
$me = new static($compiler);
$me->addMacro('if', [$me, 'macroIf'], [$me, 'macroEndIf']);
$me->addMacro('else', [$me, 'macroElse']);
$me->addMacro('elseif', [$me, 'macroElseIf']);
$me->addMacro('ifset', 'if (isset(%node.args)) %node.line {', '}');
$me->addMacro('elseifset', [$me, 'macroElseIf']);
$me->addMacro('ifcontent', [$me, 'macroIfContent'], [$me, 'macroEndIfContent']);
$me->addMacro('ifchanged', [$me, 'macroIfChanged'], '}');
$me->addMacro('switch', '$ʟ_switch = (%node.args) %node.line; if (false) {', '}');
$me->addMacro('case', [$me, 'macroCase']);
$me->addMacro('foreach', '', [$me, 'macroEndForeach']);
$me->addMacro('iterateWhile', [$me, 'macroIterateWhile'], [$me, 'macroEndIterateWhile']);
$me->addMacro('for', 'for (%node.args) %node.line {', '}');
$me->addMacro('while', [$me, 'macroWhile'], [$me, 'macroEndWhile']);
$me->addMacro('continueIf', [$me, 'macroBreakContinueIf']);
$me->addMacro('breakIf', [$me, 'macroBreakContinueIf']);
$me->addMacro('skipIf', [$me, 'macroBreakContinueIf']);
$me->addMacro('first', 'if ($iterator->isFirst(%node.args)) %node.line {', '}');
$me->addMacro('last', 'if ($iterator->isLast(%node.args)) %node.line {', '}');
$me->addMacro('sep', 'if (!$iterator->isLast(%node.args)) %node.line {', '}');
$me->addMacro('try', [$me, 'macroTry'], '}');
$me->addMacro('rollback', [$me, 'macroRollback']);
$me->addMacro('var', [$me, 'macroVar']);
$me->addMacro('default', [$me, 'macroVar']);
$me->addMacro('dump', [$me, 'macroDump']);
$me->addMacro('debugbreak', [$me, 'macroDebugbreak']);
$me->addMacro('trace', 'LR\Tracer::throw() %node.line;');
$me->addMacro('l', '?>{<?php');
$me->addMacro('r', '?>}<?php');
$me->addMacro('_', [$me, 'macroTranslate'], [$me, 'macroTranslate']);
$me->addMacro('translate', [$me, 'macroTranslate'], [$me, 'macroTranslate']);
$me->addMacro('=', [$me, 'macroExpr']);
$me->addMacro('capture', [$me, 'macroCapture'], [$me, 'macroCaptureEnd']);
$me->addMacro('spaceless', [$me, 'macroSpaceless'], [$me, 'macroSpaceless']);
$me->addMacro('include', [$me, 'macroInclude']);
$me->addMacro('sandbox', [$me, 'macroSandbox']);
$me->addMacro('contentType', [$me, 'macroContentType'], null, null, self::ALLOWED_IN_HEAD);
$me->addMacro('php', [$me, 'macroExpr']);
$me->addMacro('do', [$me, 'macroExpr']);
$me->addMacro('class', null, null, [$me, 'macroClass']);
$me->addMacro('attr', null, null, [$me, 'macroAttr']);
$me->addMacro('tag', [$me, 'macroTag'], [$me, 'macroTagEnd']);
$me->addMacro('parameters', [$me, 'macroParameters'], null, null, self::ALLOWED_IN_HEAD);
$me->addMacro('varType', [$me, 'macroVarType'], null, null, self::ALLOWED_IN_HEAD);
$me->addMacro('varPrint', [$me, 'macroVarPrint'], null, null, self::ALLOWED_IN_HEAD);
$me->addMacro('templateType', [$me, 'macroTemplateType'], null, null, self::ALLOWED_IN_HEAD);
$me->addMacro('templatePrint', [$me, 'macroTemplatePrint'], null, null, self::ALLOWED_IN_HEAD);
}
/**
* Initializes before template parsing.
* @return void
*/
public function initialize()
{
$this->overwrittenVars = [];
$this->idCounter = 0;
}
/**
* Finishes template parsing.
*/
public function finalize()
{
if ($this->printTemplate) {
return ["(new Latte\\Runtime\\Blueprint)->printClass(\$this, {$this->printTemplate}); exit;"];
}
$code = '';
if ($this->overwrittenVars) {
$vars = array_map(function ($l) { return implode(', ', $l); }, $this->overwrittenVars);
$code .= 'foreach (array_intersect_key(' . Latte\PhpHelpers::dump($vars) . ', $this->params) as $ʟ_v => $ʟ_l) { '
. 'trigger_error("Variable \$$ʟ_v overwritten in foreach on line $ʟ_l"); } ';
}
$code = $code
? 'if (!$this->getReferringTemplate() || $this->getReferenceType() === "extends") { ' . $code . '}'
: '';
return [$code];
}
/********************* macros ****************d*g**/
/**
* {if ...}
*/
public function macroIf(MacroNode $node, PhpWriter $writer): string
{
$node->validate(null);
if ($node->data->capture = ($node->args === '')) {
return $writer->write('ob_start(function () {}) %node.line; try {');
}
if ($node->prefix === $node::PREFIX_TAG) {
for ($id = 0, $tmp = $node->htmlNode; $tmp = $tmp->parentNode; $id++);
$node->htmlNode->data->id = $node->htmlNode->data->id ?? $id;
return $writer->write(
$node->htmlNode->closing
? 'if ($ʟ_if[%var]) %node.line {'
: 'if ($ʟ_if[%var] = (%node.args)) %node.line {',
$node->htmlNode->data->id
);
}
return $writer->write('if (%node.args) %node.line {');
}
/**
* {/if ...}
*/
public function macroEndIf(MacroNode $node, PhpWriter $writer): string
{
if (!$node->data->capture) {
return '}';
}
$node->validate('condition');
if (isset($node->data->else)) {
return $writer->write('
} finally {
$ʟ_ifB = ob_get_clean();
}
} finally {
$ʟ_ifA = ob_get_clean();
}
echo (%node.args) ? $ʟ_ifA : $ʟ_ifB %node.line;
');
}
return $writer->write('
} finally {
$ʟ_ifA = ob_get_clean();
}
if (%node.args) %node.line { echo $ʟ_ifA; }
');
}
/**
* {else}
*/
public function macroElse(MacroNode $node, PhpWriter $writer): string
{
if ($node->args !== '' && Helpers::startsWith($node->args, 'if')) {
throw new CompileException('Arguments are not allowed in {else}, did you mean {elseif}?');
}
$node->validate(false, ['if', 'ifset', 'foreach', 'ifchanged', 'try', 'first', 'last', 'sep']);
$parent = $node->parentNode;
if (isset($parent->data->else)) {
throw new CompileException('Tag ' . $parent->getNotation() . ' may only contain one {else} clause.');
}
$parent->data->else = true;
if ($parent->name === 'if' && $parent->data->capture) {
return $writer->write('ob_start(function () {}) %node.line; try {');
} elseif ($parent->name === 'foreach') {
return $writer->write('$iterations++; } if ($iterator->isEmpty()) %node.line {');
} elseif ($parent->name === 'ifchanged' && $parent->data->capture) {
$res = '?>' . $parent->closingCode . $writer->write('<?php else %node.line {');
$parent->closingCode = '<?php } ?>';
return $res;
} elseif ($parent->name === 'try') {
$node->openingCode = $parent->data->codeCatch;
$parent->closingCode = $parent->data->codeFinally;
return '';
}
return $writer->write('} else %node.line {');
}
/**
* {elseif}
* {elseifset}
*/
public function macroElseIf(MacroNode $node, PhpWriter $writer): string
{
$node->validate(true, ['if', 'ifset']);
if (isset($node->parentNode->data->else) || !empty($node->parentNode->data->capture)) {
throw new CompileException('Tag ' . $node->getNotation() . ' is unexpected here.');
}
return $writer->write($node->name === 'elseif'
? '} elseif (%node.args) %node.line {'
: '} elseif (isset(%node.args)) %node.line {');
}
/**
* n:ifcontent
*/
public function macroIfContent(MacroNode $node, PhpWriter $writer): void
{
if (!$node->prefix || $node->prefix !== MacroNode::PREFIX_NONE) {
throw new CompileException("Unknown {$node->getNotation()}, use n:{$node->name} attribute.");
}
if ($node->htmlNode->empty) {
trigger_error("Unnecessary n:ifcontent on empty element <{$node->htmlNode->name}> (on line {$this->getCompiler()->getLine()})", E_USER_DEPRECATED);
}
$node->validate(false);
}
/**
* n:ifcontent
*/
public function macroEndIfContent(MacroNode $node, PhpWriter $writer): void
{
$id = ++$this->idCounter;
$node->openingCode = '<?php ob_start(function () {}); try { ?>';
$node->innerContent = '<?php ob_start(); try { ?>'
. $node->innerContent
. "<?php } finally { \$ʟ_ifc[$id] = rtrim(ob_get_flush()) === ''; } ?>";
$node->closingCode = "<?php } finally { if (\$ʟ_ifc[$id] ?? null) { ob_end_clean(); } else { echo ob_get_clean(); } } ?>";
}
/**
* {ifchanged [...]}
*/
public function macroIfChanged(MacroNode $node, PhpWriter $writer): void
{
$node->validate(null);
$id = $node->data->id = ++$this->idCounter;
if ($node->data->capture = ($node->args === '')) {
$node->openingCode = $writer->write('<?php ob_start(function () {}); try %node.line { ?>');
$node->closingCode =
'<?php } finally { $ʟ_tmp = ob_get_clean(); } '
. "if ((\$ʟ_loc[$id] ?? null) !== \$ʟ_tmp) { echo \$ʟ_loc[$id] = \$ʟ_tmp; } ?>";
} else {
$node->openingCode = $writer->write(
'<?php if (($ʟ_loc[%0_var] ?? null) !== ($ʟ_tmp = [%node.args])) { $ʟ_loc[%0_var] = $ʟ_tmp; ?>',
$id
);
}
}
/**
* {try}
*/
public function macroTry(MacroNode $node, PhpWriter $writer): void
{
$node->replaced = false;
$node->validate(false);
for ($id = 0, $tmp = $node; $tmp = $tmp->closest(['try']); $id++);
$node->data->codeCatch = '<?php
} catch (Throwable $ʟ_e) {
ob_end_clean();
if (!($ʟ_e instanceof LR\RollbackException) && isset($this->global->coreExceptionHandler)) {
($this->global->coreExceptionHandler)($ʟ_e, $this);
}
?>';
$node->data->codeFinally = $writer->write('<?php
ob_start();
} finally {
echo ob_get_clean();
$iterator = $ʟ_it = $ʟ_try[%0_var][0];
} ?>', $id);
$node->openingCode = $writer->write('<?php $ʟ_try[%var] = [$ʟ_it ?? null]; ob_start(function () {}); try %node.line { ?>', $id);
$node->closingCode = $node->data->codeCatch . $node->data->codeFinally;
}
/**
* {rollback}
*/
public function macroRollback(MacroNode $node, PhpWriter $writer): string
{
$parent = $node->closest(['try']);
if (!$parent || isset($parent->data->catch)) {
throw new CompileException('Tag {rollback} must be inside {try} ... {/try}.');
}
$node->validate(false);
return $writer->write('throw new LR\RollbackException;');
}
/**
* {_$var |modifiers}
* {translate|modifiers}
*/
public function macroTranslate(MacroNode $node, PhpWriter $writer): string
{
if ($node->closing) {
if (strpos($node->content, '<?php') === false) {
$tmp = $node->content;
$node->content = '';
return $writer->write(
'$ʟ_fi = new LR\FilterInfo(%var);
echo %modifyContent($this->filters->filterContent("translate", $ʟ_fi, %raw)) %node.line;',
implode('', $node->context),
PhpHelpers::dump($tmp)
);
}
$node->openingCode = '<?php ob_start(function () {}); try { ?>' . $node->openingCode;
return $writer->write(
'} finally {
$ʟ_tmp = ob_get_clean();
}
$ʟ_fi = new LR\FilterInfo(%var);
echo %modifyContent($this->filters->filterContent("translate", $ʟ_fi, $ʟ_tmp)) %node.line;',
implode('', $node->context)
);
} elseif ($node->empty = ($node->args !== '') && $node->name === '_') {
return $writer->write('echo %modify(($this->filters->translate)(%node.args)) %node.line;');
} elseif ($node->name === '_') {
trigger_error("As a pair tag for translation, {translate} ... {/translate} should be used instead of {_} ... {/} (on line $node->startLine)", E_USER_DEPRECATED);
}
return '';
}
/**
* {include [file] "file" [with blocks] [,] [params]}
*/
public function macroInclude(MacroNode $node, PhpWriter $writer): string
{
[$file] = $node->tokenizer->fetchWordWithModifier('file');
$mode = 'include';
if ($node->tokenizer->isNext('with') && !$node->tokenizer->isPrev(',')) {
$node->tokenizer->consumeValue('with');
$node->tokenizer->consumeValue('blocks');
$mode = 'includeblock';
}
$node->replaced = false;
$noEscape = Helpers::removeFilter($node->modifiers, 'noescape');
if ($node->modifiers && !$noEscape) {
$node->modifiers .= '|escape';
}
return $writer->write(
'$this->createTemplate(%word, %node.array? + $this->params, %var)->renderToContentType(%raw) %node.line;',
$file,
$mode,
$node->modifiers
? $writer->write('function ($s, $type) { $ʟ_fi = new LR\FilterInfo($type); return %modifyContent($s); }')
: PhpHelpers::dump($noEscape ? null : implode('', $node->context))
);
}
/**
* {sandbox "file" [,] [params]}
*/
public function macroSandbox(MacroNode $node, PhpWriter $writer): string
{
$node->validate(null);
$node->replaced = false;
return $writer->write(
'ob_start(function () {});
try { $this->createTemplate(%node.word, %node.array, "sandbox")->renderToContentType(%var) %node.line; echo ob_get_clean(); }
catch (\Throwable $ʟ_e) {
if (isset($this->global->coreExceptionHandler)) { ob_end_clean(); ($this->global->coreExceptionHandler)($ʟ_e, $this); }
else { echo ob_get_clean(); throw $ʟ_e; }
}',
implode('', $node->context)
);
}
/**
* {capture $variable}
*/
public function macroCapture(MacroNode $node, PhpWriter $writer): string
{
$variable = $node->tokenizer->fetchWord();
if (!$variable) {
throw new CompileException('Missing variable in {capture}.');
} elseif (!Helpers::startsWith($variable, '$')) {
throw new CompileException("Invalid capture block variable '$variable'");
}
$this->checkExtraArgs($node);
$node->data->variable = $variable;
return $writer->write('ob_start(function () {}) %node.line; try {');
}
/**
* {/capture}
*/
public function macroCaptureEnd(MacroNode $node, PhpWriter $writer): string
{
$body = in_array(implode('', $node->context), [Engine::CONTENT_HTML, Engine::CONTENT_XHTML], true)
? 'ob_get_length() ? new LR\\Html(ob_get_clean()) : ob_get_clean()'
: 'ob_get_clean()';
return $writer->write(
'} finally {
$ʟ_tmp = %raw;
}
$ʟ_fi = new LR\FilterInfo(%var); %raw = %modifyContent($ʟ_tmp);',
$body,
implode('', $node->context),
$node->data->variable
);
}
/**
* {spaceless} ... {/spaceless}
*/
public function macroSpaceless(MacroNode $node, PhpWriter $writer): void
{
$node->validate(false);
$node->openingCode = $writer->write(in_array($node->context[0], [Engine::CONTENT_HTML, Engine::CONTENT_XHTML], true)
? "<?php ob_start('Latte\\Runtime\\Filters::spacelessHtmlHandler', 4096) %node.line; try { ?>"
: "<?php ob_start('Latte\\Runtime\\Filters::spacelessText', 4096) %node.line; try { ?>");
$node->closingCode = '<?php } finally { ob_end_flush(); } ?>';
}
/**
* {while ...}
*/
public function macroWhile(MacroNode $node, PhpWriter $writer): string
{
$node->validate(null);
if ($node->data->do = ($node->args === '')) {
return $writer->write('do %node.line {');
}
return $writer->write('while (%node.args) %node.line {');
}
/**
* {/while ...}
*/
public function macroEndWhile(MacroNode $node, PhpWriter $writer): string
{
if ($node->data->do) {
$node->validate(true);
return $writer->write('} while (%node.args);');
}
return '}';
}
/**
* {foreach ...}
*/
public function macroEndForeach(MacroNode $node, PhpWriter $writer): void
{
$noCheck = Helpers::removeFilter($node->modifiers, 'nocheck');
$noIterator = Helpers::removeFilter($node->modifiers, 'noiterator');
if ($node->modifiers) {
throw new CompileException('Only modifiers |noiterator and |nocheck are allowed here.');
}
$node->validate(true);
$node->openingCode = '<?php $iterations = 0; ';
$args = $writer->formatArgs();
if (!$noCheck) {
preg_match('#.+\s+as\s*\$(\w+)(?:\s*=>\s*\$(\w+))?#i', $args, $m);
for ($i = 1; $i < count($m); $i++) {
$this->overwrittenVars[$m[$i]][] = $node->startLine;
}
}
if (
!$noIterator
&& preg_match('#\$iterator\W|\Wget_defined_vars\W#', $this->getCompiler()->expandTokens($node->content))
) {
$args = preg_replace('#(.*)\s+as\s+#i', '$1, $ʟ_it ?? null) as ', $args, 1);
$node->openingCode .= $writer->write('foreach ($iterator = $ʟ_it = new LR\CachingIterator(%raw) %node.line { ?>', $args);
$node->closingCode = '<?php $iterations++; } $iterator = $ʟ_it = $ʟ_it->getParent(); ?>';
} else {
$node->openingCode .= $writer->write('foreach (%raw) %node.line { ?>', $args);
$node->closingCode = '<?php $iterations++; } ?>';
}
}
/**
* {iterateWhile ...}
*/
public function macroIterateWhile(MacroNode $node, PhpWriter $writer): void
{
if (!$node->closest(['foreach'])) {
throw new CompileException('Tag ' . $node->getNotation() . ' must be inside {foreach} ... {/foreach}.');
}
$node->data->begin = $node->args !== '';
}
/**
* {/iterateWhile ...}
*/
public function macroEndIterateWhile(MacroNode $node, PhpWriter $writer): void
{
$node->validate(true);
$foreach = $node->closest(['foreach']);
$vars = preg_replace('#^.+\s+as\s+(?:(.+)=>)?(.+)$#i', '$1, $2', $foreach->args);
$stmt = '
if (!$iterator->hasNext()' . ($node->args ? $writer->write(' || !(%node.args)') : '') . ') {
break;
}
$iterator->next();
[' . $vars . '] = [$iterator->key(), $iterator->current()];
';
if ($node->data->begin) {
$node->openingCode = $writer->write('<?php do %node.line { %raw ?>', $stmt);
$node->closingCode = '<?php } while (true); ?>';
} else {
$node->openingCode = $writer->write('<?php do %node.line { ?>');
$node->closingCode = "<?php $stmt } while (true); ?>";
}
}
/**
* {breakIf ...}
* {continueIf ...}
* {skipIf ...}
*/
public function macroBreakContinueIf(MacroNode $node, PhpWriter $writer): string
{
if ($node->name === 'skipIf') {
$ancestors = ['foreach'];
$cmd = '{ $iterator->skipRound(); continue; }';
} else {
$ancestors = ['for', 'foreach', 'while'];
$cmd = str_replace('If', '', $node->name);
}
if (!$node->closest($ancestors)) {
throw new CompileException('Tag ' . $node->getNotation() . ' is unexpected here.');
}
$node->validate('condition');
if ($node->parentNode->prefix === $node::PREFIX_NONE) {
return $writer->write("if (%node.args) %node.line { echo \"</{$node->parentNode->htmlNode->name}>\\n\"; $cmd; }");
}
return $writer->write("if (%node.args) %node.line $cmd;");
}
/**
* n:class="..."
*/
public function macroClass(MacroNode $node, PhpWriter $writer): string
{
if (isset($node->htmlNode->attrs['class'])) {
throw new CompileException('It is not possible to combine class with n:class.');
}
$node->validate(true);
return $writer->write('echo ($ʟ_tmp = array_filter(%node.array)) ? \' class="\' . %escape(implode(" ", array_unique($ʟ_tmp))) . \'"\' : "" %node.line;');
}
/**
* n:attr="..."
*/
public function macroAttr(MacroNode $node, PhpWriter $writer): string
{
$node->validate(true);
return $writer->write('$ʟ_tmp = %node.array; echo LR\Filters::htmlAttributes(isset($ʟ_tmp[0]) && is_array($ʟ_tmp[0]) ? $ʟ_tmp[0] : $ʟ_tmp) %node.line;');
}
/**
* n:tag="..."
*/
public function macroTag(MacroNode $node, PhpWriter $writer): void
{
if (!$node->prefix || $node->prefix !== MacroNode::PREFIX_NONE) {
throw new CompileException("Unknown {$node->getNotation()}, use n:{$node->name} attribute.");
} elseif (preg_match('(style$|script$)iA', $node->htmlNode->name)) {
throw new CompileException("Attribute {$node->getNotation()} is not allowed in <script> or <style>");
}
$node->validate(true);
}
/**
* n:tag="..."
*/
public function macroTagEnd(MacroNode $node, PhpWriter $writer): void
{
for ($id = 0, $tmp = $node->htmlNode; $tmp = $tmp->parentNode; $id++);
$node->htmlNode->data->id = $node->htmlNode->data->id ?? $id;
$node->openingCode = $writer->write('<?php
$ʟ_tag[%0_var] = (%node.args) ?? %1_var;
Latte\Runtime\Filters::checkTagSwitch(%1_var, $ʟ_tag[%0_var]);
?>', $node->htmlNode->data->id, $node->htmlNode->name);
$node->content = preg_replace(
'~^(\s*<)' . Latte\Parser::RE_TAG_NAME . '~',
"\$1<?php echo \$ʟ_tag[{$node->htmlNode->data->id}]; ?>\n",
$node->content
);
$node->content = preg_replace(
'~</' . Latte\Parser::RE_TAG_NAME . '(\s*>\s*)$~',
"</<?php echo \$ʟ_tag[{$node->htmlNode->data->id}]; ?>\n\$1",
$node->content
);
}
/**
* {dump ...}
*/
public function macroDump(MacroNode $node, PhpWriter $writer): string
{
$node->validate(null);
$args = $writer->formatArgs();
return $writer->write(
'Tracy\Debugger::barDump(' . ($args ? "($args)" : 'get_defined_vars()') . ', %var) %node.line;',
$args ?: 'variables'
);
}
/**
* {debugbreak ...}
*/
public function macroDebugbreak(MacroNode $node, PhpWriter $writer): string
{
$node->validate(null);
if (function_exists($func = 'debugbreak') || function_exists($func = 'xdebug_break')) {
return $writer->write(($node->args === '' ? '' : 'if (%node.args) ') . "$func() %node.line;");
}
return '';
}
/**
* {case ...}
*/
public function macroCase(MacroNode $node, PhpWriter $writer): string
{
$node->validate(true, ['switch']);
if (isset($node->parentNode->data->default)) {
throw new CompileException('Tag {default} must follow after {case} clause.');
}
return $writer->write('} elseif (in_array($ʟ_switch, %node.array, true)) %node.line {');
}
/**
* {var ...}
* {default ...}
* {default} in {switch}
*/
public function macroVar(MacroNode $node, PhpWriter $writer): string
{
if ($node->name === 'default' && $node->parentNode && $node->parentNode->name === 'switch') {
$node->validate(false, ['switch']);
if (isset($node->parentNode->data->default)) {
throw new CompileException('Tag {switch} may only contain one {default} clause.');
}
$node->parentNode->data->default = true;
return $writer->write('} else %node.line {');
} elseif ($node->modifiers) {
$node->setArgs($node->args . $node->modifiers);
$node->modifiers = '';
}
$node->validate(true);
$var = true;
$hasType = false;
$tokens = $node->tokenizer;
$res = new Latte\MacroTokens;
while ($tokens->nextToken()) {
if (
$var
&& $tokens->isCurrent($tokens::T_SYMBOL)
&& (
$tokens->isNext(',', '=>', '=')
|| !$tokens->isNext(...$tokens::SIGNIFICANT)
)
) {
trigger_error("Inside tag {{$node->name} {$node->args}} should be '{$tokens->currentValue()}' replaced with '\${$tokens->currentValue()}' (on line $node->startLine)", E_USER_DEPRECATED);
} elseif ($var && !$hasType && $tokens->isCurrent($tokens::T_SYMBOL, '?', 'null', '\\')) { // type
$tokens->nextToken();
$tokens->nextAll($tokens::T_SYMBOL, '\\', '|', '[', ']', 'null');
$hasType = true;
continue;
}
if ($var && $tokens->isCurrent($tokens::T_SYMBOL, $tokens::T_VARIABLE)) {
if ($node->name === 'default') {
$res->append("'" . ltrim($tokens->currentValue(), '$') . "'");
} else {
$res->append('$' . ltrim($tokens->currentValue(), '$'));
}
$var = null;
} elseif ($tokens->isCurrent('=', '=>') && $tokens->depth === 0) {
if ($tokens->isCurrent('=>')) {
trigger_error("Inside tag {{$node->name} {$node->args}} should be '=>' replaced with '=' (on line $node->startLine)", E_USER_DEPRECATED);
}
$res->append($node->name === 'default' ? '=>' : '=');
$var = false;
} elseif ($tokens->isCurrent(',', ';') && $tokens->depth === 0) {
if ($tokens->isCurrent(';')) {
trigger_error("Inside tag {{$node->name} {$node->args}} should be ';' replaced with ',' (on line $node->startLine)", E_USER_DEPRECATED);
}
if ($var === null) {
$res->append($node->name === 'default' ? '=>null' : '=null');
}
$res->append($node->name === 'default' ? ',' : ';');
$var = true;
$hasType = false;
} elseif ($var === null && $node->name === 'default' && !$tokens->isCurrent($tokens::T_WHITESPACE)) {
throw new CompileException("Unexpected '{$tokens->currentValue()}' in {default $node->args}");
} else {
$res->append($tokens->currentToken());
}
}
if ($var === null) {
$res->append($node->name === 'default' ? '=>null' : '=null');
}
$res = $writer->preprocess($res);
$writer->validateKeywords($res);
$out = $writer->quotingPass($res)->joinAll();
return $writer->write($node->name === 'default'
? 'extract([%raw], EXTR_SKIP) %node.line;'
: '%raw %node.line;', $out);
}
/**
* {= ...}
* {php ...}
* {do ...}
*/
public function macroExpr(MacroNode $node, PhpWriter $writer): string
{
$node->validate(true, [], $node->name === '=');
if ($node->name !== 'php') {
$tokens = $node->tokenizer;
while ($tokens->nextToken()) {
if ($tokens->isCurrent(';') && $tokens->depth === 0) {
trigger_error("The use of character ';' is deprecated in tag {{$node->name}}, try to use {php} (on line $node->startLine)", E_USER_DEPRECATED);
break;
}
}
$tokens->reset();
}
return $writer->write(
$node->name === '='
? 'echo %modify(%node.args) %node.line;'
: '%modify(%node.args) %node.line;'
);
}
/**
* {contentType ...}
*/
public function macroContentType(MacroNode $node, PhpWriter $writer): string
{
$node->validate(true);
if (
!$this->getCompiler()->isInHead()
&& !($node->htmlNode && strtolower($node->htmlNode->name) === 'script' && strpos($node->args, 'html') !== false)
) {
throw new CompileException($node->getNotation() . ' is allowed only in template header.');
}
$compiler = $this->getCompiler();
if (strpos($node->args, 'xhtml') !== false) {
$type = $compiler::CONTENT_XHTML;
} elseif (strpos($node->args, 'html') !== false) {
$type = $compiler::CONTENT_HTML;
} elseif (strpos($node->args, 'xml') !== false) {
$type = $compiler::CONTENT_XML;
} elseif (strpos($node->args, 'javascript') !== false) {
$type = $compiler::CONTENT_JS;
} elseif (strpos($node->args, 'css') !== false) {
$type = $compiler::CONTENT_CSS;
} elseif (strpos($node->args, 'calendar') !== false) {
$type = $compiler::CONTENT_ICAL;
} else {
$type = $compiler::CONTENT_TEXT;
}
$compiler->setContentType($type);
if (strpos($node->args, '/') && !$node->htmlNode) {
return $writer->write(
'if (empty($this->global->coreCaptured) && in_array($this->getReferenceType(), ["extends", null], true)) { header(%var) %node.line; } ',
'Content-Type: ' . $node->args
);
}
return '';
}
/**
* {parameters type $var, ...}
*/
public function macroParameters(MacroNode $node, PhpWriter $writer): void
{
if (!$this->getCompiler()->isInHead()) {
throw new CompileException($node->getNotation() . ' is allowed only in template header.');
}
if ($node->modifiers) {
$node->setArgs($node->args . $node->modifiers);
$node->modifiers = '';
}
$node->validate(true);
$tokens = $node->tokenizer;
$writer->validateKeywords($tokens);
$params = [];
while ($tokens->isNext(...$tokens::SIGNIFICANT)) {
if ($tokens->nextToken($tokens::T_SYMBOL, '?', 'null', '\\')) { // type
$tokens->nextAll($tokens::T_SYMBOL, '\\', '|', '[', ']', 'null');
}
$param = $tokens->consumeValue($tokens::T_VARIABLE);
$default = $tokens->nextToken('=')
? $tokens->joinUntilSameDepth(',')
: 'null';
$params[] = $writer->write(
'%raw = $this->params[%var] ?? $this->params[%var] ?? %raw;',
$param,
count($params),
substr($param, 1),
$default
);
if ($tokens->isNext(...$tokens::SIGNIFICANT)) {
$tokens->consumeValue(',');
}
}
$this->getCompiler()->paramsExtraction = implode('', $params);
}
/**
* {varType type $var}
*/
public function macroVarType(MacroNode $node): void
{
if ($node->modifiers) {
$node->setArgs($node->args . $node->modifiers);
$node->modifiers = '';
}
$node->validate(true);
$type = trim($node->tokenizer->joinUntil($node->tokenizer::T_VARIABLE));
$variable = $node->tokenizer->nextToken($node->tokenizer::T_VARIABLE);
if (!$type || !$variable) {
throw new CompileException('Unexpected content, expecting {varType type $var}.');
}
}
/**
* {varPrint [all]}
*/
public function macroVarPrint(MacroNode $node): string
{
$vars = $node->tokenizer->fetchWord() === 'all'
? 'get_defined_vars()'
: 'array_diff_key(get_defined_vars(), $this->getParameters())';
return "(new Latte\\Runtime\\Blueprint)->printVars($vars); exit;";
}
/**
* {templateType ClassName}
*/
public function macroTemplateType(MacroNode $node): void
{
if (!$this->getCompiler()->isInHead()) {
throw new CompileException($node->getNotation() . ' is allowed only in template header.');
}
$node->validate('class name');
}
/**
* {templatePrint [ClassName]}
*/
public function macroTemplatePrint(MacroNode $node): void
{
$this->printTemplate = PhpHelpers::dump($node->tokenizer->fetchWord() ?: null);
}
}