/** * @dataProvider sourcesProvider */ public function testCompiles($source, $expectedOutput, $locals) { // // parse // $parseContext = new ParserContext(); $parseContext->setString($source)->setBaseDirectory(__DIR__); $template = $parseContext->parse(); $this->assertInstanceOf('Skrz\\Templating\\Engine\\AST\\TemplateNode', $template); // // compile // $compilerContext = new CompilerContext(); $compilerContext->setNamespace('Skrz\\Templating\\Engine')->setClassName("Template_" . sha1(uniqid('', true)))->setTemplate($template)->setParserContext($parseContext)->addModifier("strip_tags", function (Compiler $compiler, ModifierNode $modifier, $text) { return new ModifierCompilerResult("strip_tags({$text})"); })->addFunction("inferno", function (Compiler $compiler, FunctionNode $function) { /** @var StatementAndExpressionVO $hell */ $hell = $compiler->walk($function->getArgument("hell")); $ret = ""; $ret .= "echo '666 ';"; $ret .= $hell->getStatement(); $ret .= "echo " . $hell->getExpression() . ";"; $ret .= "echo ' 666';"; return new FunctionCompilerResult($ret); }); $compiled = $compilerContext->compile(); // // debug // // echo "\n\n"; // echo "$source\n"; // echo "--------\n"; // echo $compiled; // echo "\n"; // // run // $this->assertNull(eval("?>{$compiled}")); $fullyQualifiedClassName = $compilerContext->getFullyQualifiedClassName(); $templateInstance = new $fullyQualifiedClassName(); ob_start(); $templateInstance($locals); $actualOutput = ob_get_contents(); ob_end_clean(); $this->assertEquals($expectedOutput, $actualOutput); }
protected function walkExpression(ExpressionNode $expression) { $ret = $this->walk($expression->getExpression()); $statements = array(); foreach ($expression->getModifiers() as $modifier) { $modifierCompiler = $this->context->getModifier($modifier->getName()); if ($modifierCompiler === null) { if (!function_exists($modifier->getName())) { throw new CompilerException($this->context, "Modifier `{$modifier->getName()}` not registered in compiler context. Template in " . $modifier->getFileName() . " @ " . $modifier->getRow() . ":" . $modifier->getColumn()); } else { $modifierCompiler = function (Compiler $compiler, ModifierNode $modifier, $expressionCode) { return new ModifierCompilerResult($modifier->getName() . "({$expressionCode} " . ($modifier->hasArgument(0) ? ", " : "") . implode(", ", $compiler->walkEach($modifier->getArguments())) . ")"); }; } } $modifierResult = $modifierCompiler($this, $modifier, $ret); if (!$modifierResult instanceof ModifierCompilerResult) { throw new CompilerException($this->context, "Modifier `{$modifier->getName()}` did not return " . __NAMESPACE__ . "\\ModifierCompilerResult."); } $statements[] = $modifierResult->getStatement(); $ret = $modifierResult->getExpression(); } return new StatementAndExpressionVO(implode("", $statements), $ret); }
/** * Prepare some of default smarty modifiers */ public static function prepareModifiers(CompilerContext $compilerContext) { $compilerContext->addModifier("truncate", function (Compiler $compiler, ModifierNode $modifier, $expressionCode) { return new ModifierCompilerResult("\\Skrz\\Templating\\Engine\\Helpers::truncate({$expressionCode}" . ($modifier->hasArgument(0) ? "," . implode(",", array_map(function (PHPNode $php) { return $php->getCode(); }, $modifier->getArguments())) : "") . ")"); }); $compilerContext->addModifier("strip_tags", function (Compiler $compiler, ModifierNode $modifier, $expressionCode) { if (!$modifier->hasArgument(0) || $modifier->hasArgument(0) && strcasecmp($modifier->getArgument(0)->getCode(), "true") === 0) { return new ModifierCompilerResult("preg_replace('/<[^>]*?>/', ' ', {$expressionCode})"); } else { return new ModifierCompilerResult("strip_tags({$expressionCode})"); } }); $compilerContext->addModifier("escape", function (Compiler $compiler, ModifierNode $modifier, $expressionCode) { $type = strtolower(trim($modifier->hasArgument(0) ? $modifier->getArgument(0)->getCode() : "html", "\"'() \t\r\n")); switch ($type) { case "html": return new ModifierCompilerResult("htmlspecialchars({$expressionCode}, ENT_QUOTES)"); case "url": return new ModifierCompilerResult("rawurlencode({$expressionCode})"); case "javascript": return new ModifierCompilerResult("\\Skrz\\Templating\\Engine\\Helpers::escapeJavascript({$expressionCode})"); default: throw new CompilerException($compiler->getContext(), "Only `html`, `url`, and `javascript` modes supported for `truncate` modifier, `{$type}` supplied. Template in " . $modifier->getFileName() . " @ " . $modifier->getRow() . ":" . $modifier->getColumn()); } }); $compilerContext->addModifier("default", function (Compiler $compiler, ModifierNode $modifier, $expressionCode) { $resultSym = $compiler->gensym(); return new ModifierCompilerResult($resultSym, "{$resultSym} = @({$expressionCode}); " . "if ({$resultSym} == null) { {$resultSym} = " . ($modifier->hasArgument(0) ? $compiler->walk($modifier->getArgument(0)) : "''") . "; }"); }); $compilerContext->addModifier("replace", function (Compiler $compiler, ModifierNode $modifier, $expressionCode) { return new ModifierCompilerResult("str_replace(" . $compiler->walk($modifier->getArgument(0)) . ", " . $compiler->walk($modifier->getArgument(1)) . ", " . $expressionCode . ")"); }); $compilerContext->addModifier("count", function (Compiler $compiler, ModifierNode $modifier, $expressionCode) { return new ModifierCompilerResult("count({$expressionCode})"); }); $compilerContext->addModifier("mb_ucfirst", function (Compiler $compiler, ModifierNode $modifier, $expressionCode) { $resultSym = $compiler->gensym(); return new ModifierCompilerResult("(mb_strtoupper(mb_substr({$resultSym}, 0, 1)) . mb_substr({$resultSym}, 1))", "{$resultSym} = {$expressionCode};"); }); $compilerContext->addModifier("mb_lcfirst", function (Compiler $compiler, ModifierNode $modifier, $expressionCode) { $resultSym = $compiler->gensym(); return new ModifierCompilerResult("(mb_strtolower(mb_substr({$resultSym}, 0, 1)) . mb_substr({$resultSym}, 1))", "{$resultSym} = {$expressionCode};"); }); $compilerContext->addModifier("string_format", function (Compiler $compiler, ModifierNode $modifier, $expressionCode) { return new ModifierCompilerResult("sprintf(" . $compiler->walk($modifier->getArgument(0)) . ", {$expressionCode})"); }); // TODO: add needed modifiers }