diff --git a/sandbox/fenom.php b/sandbox/fenom.php index e2afb39..39ff1f3 100644 --- a/sandbox/fenom.php +++ b/sandbox/fenom.php @@ -1,13 +1,23 @@ display("../templates/../fenom.php", array( - "user" => array( - "name" => "Ivka", + $fenom = Fenom::factory(__DIR__.'/templates', __DIR__.'/compiled', Fenom::FORCE_COMPILE); + + $fenom->display("greeting.tpl", array( + "user" => array( + "name" => "Ivka", + 'type' => 'new' + ), 'type' => 'new' - ), - 'type' => 'new' -)); \ No newline at end of file + )); +} \ No newline at end of file diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index cf39652..e33dfa8 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -23,7 +23,8 @@ use Fenom\Error\TokenizeException; */ class Template extends Render { - + const VAR_NAME = '$var'; + const TPL_NAME = '$tpl'; /** * Disable array parser. */ @@ -440,11 +441,8 @@ class Template extends Render { if (!$this->_code) { // evaluate template's code -// $code = ("\$this->_code = " . $this->_getClosureSource() . ";\n\$this->_macros = " . $this->_getMacrosArray() . ';'); -// file_put_contents('/tmp/last.tpl', $code); eval("\$this->_code = " . $this->_getClosureSource() . ";\n\$this->_macros = " . $this->_getMacrosArray() . ';'); if (!$this->_code) { -// exit; throw new CompileException("Fatal error while creating the template"); } } @@ -552,7 +550,8 @@ class Template extends Render * @static * @param Tokenizer $tokens * @throws \LogicException - * @throws TokenizeException + * @throws \RuntimeException + * @throws Error\TokenizeException * @return string */ public function parseAct(Tokenizer $tokens) @@ -573,6 +572,15 @@ class Template extends Render $name = $action . "." . $name; } return $this->parseMacroCall($tokens, $name); + } elseif($tokens->is(T_DOUBLE_COLON, T_NS_SEPARATOR)) { // static method call + $tokens->back(); + $p = $tokens->p; + $static = $this->parseStatic($tokens); + if($tokens->is("(")) { + return $this->out($this->parseExpr($tokens->seek($p))); + } else { + return $this->out(Compiler::smartFuncParser($static, $tokens, $this)); + } } if ($tag = $this->_fenom->getTag($action, $this)) { // call some function @@ -766,6 +774,10 @@ class Template extends Render throw new \Exception("Function " . $tokens->getAndNext() . " not found"); } $code = $unary . $func . $this->parseArgs($tokens->next()); + } elseif($tokens->isNext(T_NS_SEPARATOR, T_DOUBLE_COLON)) { + $method = $this->parseStatic($tokens); + $args = $this->parseArgs($tokens); + $code = $unary . $method . $args; } else { return false; } @@ -1179,13 +1191,18 @@ class Template extends Render public function parseModifier(Tokenizer $tokens, $value) { while ($tokens->is("|")) { - $mods = $this->_fenom->getModifier($tokens->getNext(Tokenizer::MACRO_STRING), $this); - if (!$mods) { - throw new \Exception("Modifier " . $tokens->current() . " not found"); + $modifier = $tokens->getNext(Tokenizer::MACRO_STRING); + if($tokens->isNext(T_DOUBLE_COLON, T_NS_SEPARATOR)) { + $mods = $this->parseStatic($tokens); + } else { + $mods = $this->_fenom->getModifier($modifier, $this); + if (!$mods) { + throw new \Exception("Modifier " . $tokens->current() . " not found"); + } + $tokens->next(); } - $tokens->next(); - $args = array(); + $args = array(); while ($tokens->is(":")) { if (!$args[] = $this->parseTerm($tokens->next())) { throw new UnexpectedTokenException($tokens); @@ -1288,23 +1305,48 @@ class Template extends Render throw new InvalidUsageException("Macro '$name' require '$arg' argument"); } } -// $n = sprintf('%x_%x', crc32($this->_name), $this->i++); if ($recursive) { $recursive['recursive'] = true; return '$tpl->getMacro("' . $name . '")->__invoke('.Compiler::toArray($args).', $tpl);'; } else { -// $body = '? >' . $macro["body"] . 'tmpVar(); return $vars . ' = $var; $var = ' . Compiler::toArray($args) . ';' . PHP_EOL . '?>' . $macro["body"] . '_options & Fenom::DENY_STATICS) { + throw new \LogicException("Static methods are disabled"); + } + $tokens->skipIf(T_NS_SEPARATOR); + $name = ""; + if ($tokens->is(T_STRING)) { + $name .= $tokens->getAndNext(); + while ($tokens->is(T_NS_SEPARATOR)) { + $name .= '\\' . $tokens->next()->get(T_STRING); + $tokens->next(); + } + } + $tokens->need(T_DOUBLE_COLON)->next()->need(T_STRING); + $static = $name . "::" . $tokens->getAndNext(); + if(!is_callable($static)) { + throw new \RuntimeException("Method $static doesn't exist"); + } + return $static; + } + /** * Parse argument list * (1 + 2.3, 'string', $var, [2,4]) * - * @static * @param Tokenizer $tokens * @throws TokenizeException * @return string diff --git a/src/Fenom/Tokenizer.php b/src/Fenom/Tokenizer.php index f1d8731..2700d1f 100644 --- a/src/Fenom/Tokenizer.php +++ b/src/Fenom/Tokenizer.php @@ -617,4 +617,16 @@ class Tokenizer { return $this->curr ? $this->curr[2] : false; } + + /** + * Seek to custom element + * @param int $p + * @return $this + */ + public function seek($p) + { + $this->p = $p; + unset($this->prev, $this->curr, $this->next); + return $this; + } } diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index bd5791d..2d0ad22 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -780,12 +780,39 @@ class TemplateTest extends TestCase ); } + public function providerStatic() { + return array( + array('{Fenom\TemplateTest::multi x=3 y=4}', '12'), + array('{Fenom\TemplateTest::multi(3,4)}', '12'), + array('{12 + Fenom\TemplateTest::multi(3,4)}', '24'), + array('{12 + 3|Fenom\TemplateTest::multi:4}', '24'), + ); + } + + public function providerStaticInvalid() { + return array( + array('{Fenom\TemplateTest::multi x=3 y=4}', 'Fenom\Error\SecurityException', "Static methods are disabled", Fenom::DENY_STATICS), + array('{Fenom\TemplateTest::multi(3,4)}', 'Fenom\Error\SecurityException', "Static methods are disabled", Fenom::DENY_STATICS), + array('{12 + Fenom\TemplateTest::multi(3,4)}', 'Fenom\Error\SecurityException', "Static methods are disabled", Fenom::DENY_STATICS), + array('{12 + 3|Fenom\TemplateTest::multi:4}', 'Fenom\Error\SecurityException', "Static methods are disabled", Fenom::DENY_STATICS), + + array('{Fenom\TemplateTest::multi_invalid x=3 y=4}', 'Fenom\Error\CompileException', 'Method Fenom\TemplateTest::multi_invalid doesn\'t exist'), + array('{Fenom\TemplateTest::multi_invalid(3,4)}', 'Fenom\Error\CompileException', 'Method Fenom\TemplateTest::multi_invalid doesn\'t exist'), + array('{12 + Fenom\TemplateTest::multi_invalid(3,4)}', 'Fenom\Error\CompileException', 'Method Fenom\TemplateTest::multi_invalid doesn\'t exist'), + array('{12 + 3|Fenom\TemplateTest::multi_invalid:4}', 'Fenom\Error\CompileException', 'Method Fenom\TemplateTest::multi_invalid doesn\'t exist'), + ); + } + public function _testSandbox() { try { - var_dump($this->fenom->setOptions(Fenom::FORCE_VERIFY)->addFilter(function ($txt) {return $txt;})->compileCode('- {$a} -')->fetch(array('a' => 1))); + var_dump($this->fenom->compileCode('{Fenom\TemplateTest::multi(3,4)}')->getBody()); } catch (\Exception $e) { print_r($e->getMessage() . "\n" . $e->getTraceAsString()); + while($e->getPrevious()) { + $e = $e->getPrevious(); + print_r("\n\n".$e->getMessage() . "\n" . $e->getTraceAsString()); + } } exit; } @@ -1037,5 +1064,27 @@ class TemplateTest extends TestCase { $this->exec($code, self::getVars(), $result); } + + /** + * @group static + * @dataProvider providerStatic + */ + public function testStatic($code, $result) + { + $this->exec($code, self::getVars(), $result, true); + } + + public static function multi($x, $y = 42) { + return $x * $y; + } + + /** + * @group static-invalid + * @dataProvider providerStaticInvalid + */ + public function testStaticInvalid($code, $exception, $message, $options = 0) + { + $this->execError($code, $exception, $message, $options); + } }