diff --git a/src/Fenom.php b/src/Fenom.php
index 50173cb..b5aaa01 100644
--- a/src/Fenom.php
+++ b/src/Fenom.php
@@ -430,18 +430,18 @@ class Fenom
"float_tags" => array()
);
if (method_exists($storage, $compiler . "Open")) {
- $c["open"] = $compiler . "Open";
+ $c["open"] = array($storage, $compiler . "Open");
} else {
throw new \LogicException("Open compiler {$compiler}Open not found");
}
if (method_exists($storage, $compiler . "Close")) {
- $c["close"] = $compiler . "Close";
+ $c["close"] = array($storage, $compiler . "Close");
} else {
throw new \LogicException("Close compiler {$compiler}Close not found");
}
foreach ($tags as $tag) {
if (method_exists($storage, "tag" . $tag)) {
- $c["tags"][$tag] = "tag" . $tag;
+ $c["tags"][$tag] = array($storage, "tag" . $tag);
if ($floats && in_array($tag, $floats)) {
$c['float_tags'][$tag] = 1;
}
@@ -767,7 +767,6 @@ class Fenom
$fenom = $this; // used in template
$_tpl = include($this->_compile_dir . "/" . $file_name);
/* @var Fenom\Render $_tpl */
-// var_dump($tpl, $_tpl->isValid()); exit;
if (!($this->_options & self::AUTO_RELOAD) || ($this->_options & self::AUTO_RELOAD) && $_tpl->isValid()) {
return $_tpl;
}
diff --git a/src/Fenom/Render.php b/src/Fenom/Render.php
index 468d08a..138af26 100644
--- a/src/Fenom/Render.php
+++ b/src/Fenom/Render.php
@@ -85,6 +85,7 @@ class Render extends \ArrayObject
$this->_fenom = $fenom;
$props += self::$_props;
$this->_name = $props["name"];
+ $this->_base_name = $props["base_name"];
$this->_scm = $props["scm"];
$this->_time = $props["time"];
$this->_depends = $props["depends"];
diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php
index f5cb24a..ce85a37 100644
--- a/src/Fenom/Template.php
+++ b/src/Fenom/Template.php
@@ -99,6 +99,11 @@ class Template extends Render
private $_filters = array();
+ /**
+ * @var int crc32 of the template name
+ */
+ private $_crc = 0;
+
protected static $_tests = array(
'integer' => 'is_int(%s)',
'int' => 'is_int(%s)',
@@ -157,7 +162,8 @@ class Template extends Render
public function load($name, $compile = true)
{
$this->_name = $name;
- if ($provider = strstr($name, ":", true)) {
+ $this->_crc = crc32($this->_name);
+ if ($provider = strstr($name, ':', true)) {
$this->_scm = $provider;
$this->_base_name = substr($name, strlen($provider) + 1);
} else {
@@ -300,7 +306,7 @@ class Template extends Render
*/
public function tmpVar()
{
- return '$t' . ($this->i++);
+ return sprintf('$t%u_%d', $this->_crc, $this->i++);
}
/**
@@ -622,7 +628,7 @@ class Template extends Render
$var = false;
}
if ($tokens->is('?', '!')) {
- $term = $this->parseTernary($tokens, $term, $tokens->current());
+ $term = $this->parseTernary($tokens, $term, $var);
$var = false;
}
$exp[] = $term;
@@ -873,35 +879,59 @@ class Template extends Render
*
* @param Tokenizer $tokens
* @param $var
- * @param $type
+ * @param $is_var
* @return string
* @throws UnexpectedTokenException
*/
- public function parseTernary(Tokenizer $tokens, $var, $type)
+ public function parseTernary(Tokenizer $tokens, $var, $is_var)
{
- $empty = ($type === "?");
+ $empty = $tokens->is('?');
$tokens->next();
if ($tokens->is(":")) {
$tokens->next();
if ($empty) {
- return '(empty(' . $var . ') ? (' . $this->parseExpr($tokens) . ') : ' . $var . ')';
+ if($is_var) {
+ return '(empty(' . $var . ') ? (' . $this->parseExpr($tokens) . ') : ' . $var . ')';
+ } else {
+ return '(' . $var . ' ?: (' . $this->parseExpr($tokens) . ')';
+ }
} else {
- return '(isset(' . $var . ') ? ' . $var . ' : (' . $this->parseExpr($tokens) . '))';
+ if($is_var) {
+ return '(isset(' . $var . ') ? ' . $var . ' : (' . $this->parseExpr($tokens) . '))';
+ } else {
+ return '((' . $var . ' !== null) ? ' . $var . ' : (' . $this->parseExpr($tokens) . '))';
+ }
}
} elseif ($tokens->is(Tokenizer::MACRO_BINARY, Tokenizer::MACRO_BOOLEAN, Tokenizer::MACRO_MATH) || !$tokens->valid()) {
if ($empty) {
- return '!empty(' . $var . ')';
+ if($is_var) {
+ return '!empty(' . $var . ')';
+ } else {
+ return '(' . $var . ')';
+ }
} else {
- return 'isset(' . $var . ')';
+ if($is_var) {
+ return 'isset(' . $var . ')';
+ } else {
+ return '(' . $var . ' !== null)';
+ }
}
} else {
$expr1 = $this->parseExpr($tokens);
$tokens->need(':')->skip();
$expr2 = $this->parseExpr($tokens);
if ($empty) {
- return '(empty(' . $var . ') ? ' . $expr2 . ' : ' . $expr1 . ')';
+ if($is_var) {
+ return '(empty(' . $var . ') ? ' . $expr2 . ' : ' . $expr1 . ')';
+ } else {
+ return '(' . $var . ' ? ' . $expr1 . ' : ' . $expr2 . ')';
+ }
} else {
- return '(isset(' . $var . ') ? ' . $expr1 . ' : ' . $expr2 . ')';
+ if($is_var) {
+ return '(isset(' . $var . ') ? ' . $expr1 . ' : ' . $expr2 . ')';
+ } else {
+ return '((' . $var . ' !== null) ? ' . $expr1 . ' : ' . $expr2 . ')';
+ }
}
}
}
@@ -1035,22 +1065,23 @@ class Template extends Render
*/
public function parseScalar(Tokenizer $tokens)
{
- $_scalar = "";
$token = $tokens->key();
switch ($token) {
case T_CONSTANT_ENCAPSED_STRING:
case T_LNUMBER:
case T_DNUMBER:
- $_scalar .= $tokens->getAndNext();
+ return $tokens->getAndNext();
break;
case T_ENCAPSED_AND_WHITESPACE:
case '"':
- $_scalar .= $this->parseQuote($tokens);
+ return $this->parseQuote($tokens);
break;
+ case '$':
+ $tokens->next()->need('.')->next()->need(T_CONST)->next();
+ return 'constant('.$this->parseName($tokens).')';
default:
throw new UnexpectedTokenException($tokens);
}
- return $_scalar;
}
/**
diff --git a/tests/TestCase.php b/tests/TestCase.php
index f10c152..30aa59f 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -113,6 +113,7 @@ class TestCase extends \PHPUnit_Framework_TestCase
* @param string $result expected result.
* @param int $options
* @param bool $dump dump source and result code (for debug)
+ * @return \Fenom\Template
*/
public function exec($code, $vars, $result, $options = 0, $dump = false)
{
@@ -122,6 +123,7 @@ class TestCase extends \PHPUnit_Framework_TestCase
echo "\n========= DUMP BEGIN ===========\n" . $code . "\n--- to ---\n" . $tpl->getBody() . "\n========= DUMP END =============\n";
}
$this->assertSame(Modifier::strip($result), Modifier::strip($tpl->fetch($vars), true), "Test $code");
+ return $tpl;
}
public function execTpl($name, $code, $vars, $result, $dump = false)
@@ -270,37 +272,3 @@ class TestCase extends \PHPUnit_Framework_TestCase
);
}
}
-
-class Fake implements \ArrayAccess
-{
- public $vars;
-
- public function offsetExists($offset)
- {
- return true;
- }
-
- public function offsetGet($offset)
- {
- if ($offset == "object") {
- return new self();
- } else {
- return new self($offset);
- }
- }
-
- public function offsetSet($offset, $value)
- {
- $this->vars[$offset] = $value;
- }
-
- public function offsetUnset($offset)
- {
- unset($this->vars[$offset]);
- }
-
- public function proxy()
- {
- return implode(", ", func_get_args());
- }
-}
diff --git a/tests/cases/Fenom/CustomProviderTest.php b/tests/cases/Fenom/CustomProviderTest.php
index 137ead6..483ed1c 100644
--- a/tests/cases/Fenom/CustomProviderTest.php
+++ b/tests/cases/Fenom/CustomProviderTest.php
@@ -14,6 +14,8 @@ class CustomProviderTest extends TestCase
public function testCustom()
{
+ $this->assertTrue($this->fenom->templateExists('my:include.tpl'));
+ $this->assertFalse($this->fenom->templateExists('my:include-none.tpl'));
$this->assertRender("start: {include 'my:include.tpl'}", 'start: include template');
//$this->assertRender("start: {import 'my:macros.tpl' as ops} {ops.add a=3 b=6}");
}
diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php
index be1b236..6e8654f 100644
--- a/tests/cases/Fenom/TemplateTest.php
+++ b/tests/cases/Fenom/TemplateTest.php
@@ -229,6 +229,7 @@ class TemplateTest extends TestCase
$result4 = 'Include Welcome, Flame (flame@dev.null) template';
return array(
array('Include {include "welcome.tpl"} template', $a, $result),
+ array('Include {include "welcome.tpl"} template', $a, $result, Fenom::FORCE_INCLUDE),
array('Include {include $tpl} template', $a, $result),
array('Include {include "$tpl"} template', $a, $result),
array('Include {include "{$tpl}"} template', $a, $result),
@@ -238,7 +239,9 @@ class TemplateTest extends TestCase
array('Include {include "wel{$fragment}.tpl"} template', $a, $result),
array('Include {include "wel{$pr_fragment|lower}.tpl"} template', $a, $result),
array('Include {include "welcome.tpl" username="Flame"} template', $a, $result2),
+ array('Include {include "welcome.tpl" username="Flame"} template', $a, $result2, Fenom::FORCE_INCLUDE),
array('Include {include "welcome.tpl" email="flame@dev.null"} template', $a, $result3),
+ array('Include {include "welcome.tpl" email="flame@dev.null"} template', $a, $result3, Fenom::FORCE_INCLUDE),
array('Include {include "welcome.tpl" username="Flame" email="flame@dev.null"} template',
$a, $result4),
);
@@ -252,6 +255,40 @@ class TemplateTest extends TestCase
);
}
+ public static function providerInsert()
+ {
+ $a = array(
+ "name" => "welcome",
+ "tpl" => "welcome.tpl",
+ "fragment" => "come",
+ "pr_fragment" => "Come",
+ "pr_name" => "Welcome",
+ "username" => "Master",
+ "email" => "dev@null.net"
+ );
+ $result = 'Include Welcome, Master (dev@null.net) template';
+ return array(
+ array('Include {insert "welcome.tpl"} template', $a, $result),
+ array("Include {insert 'welcome.tpl'} template", $a, $result),
+ );
+ }
+
+ public static function providerInsertInvalid()
+ {
+ return array(
+ array('Include {insert} template', 'Fenom\Error\CompileException', "Unexpected end of expression"),
+ array('Include {insert another="welcome.tpl"} template', 'Fenom\Error\CompileException', "Template another not found"),
+ array('Include {insert $tpl} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"),
+ array('Include {insert "$tpl"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"),
+ array('Include {insert "{$tpl}"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"),
+ array('Include {insert "$name.tpl"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"),
+ array('Include {insert "{$name}.tpl"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"),
+ array('Include {insert "{$pr_name|lower}.tpl"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"),
+ array('Include {insert "wel{$fragment}.tpl"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"),
+ array('Include {insert "welcome.tpl" email="flame@dev.null"} template', 'Fenom\Error\CompileException', "Unexpected token 'email'"),
+ );
+ }
+
public static function providerIf()
{
$a = array(
@@ -783,9 +820,9 @@ class TemplateTest extends TestCase
* @group include
* @dataProvider providerInclude
*/
- public function testInclude($code, $vars, $result)
+ public function testInclude($code, $vars, $result, $options = 0)
{
- $this->exec($code, $vars, $result);
+ $this->exec($code, $vars, $result, $options);
}
/**
@@ -796,6 +833,27 @@ class TemplateTest extends TestCase
$this->execError($code, $exception, $message, $options);
}
+ /**
+ * @group insert
+ * @dataProvider providerInsert
+ */
+ public function testInsert($code, $vars, $result)
+ {
+ $this->tpl('insert.tpl', $code);
+ $this->assertRender('insert.tpl', $result, $vars);
+ $tpl = $this->exec($code, $vars, $result);
+ $this->assertTrue($tpl->isValid());
+ }
+
+ /**
+ * @group insert
+ * @dataProvider providerInsertInvalid
+ */
+ public function testInsertInvalid($code, $exception, $message, $options = 0)
+ {
+ $this->execError($code, $exception, $message, $options);
+ }
+
/**
* @dataProvider providerIf
* @group test-if
diff --git a/tests/cases/FenomTest.php b/tests/cases/FenomTest.php
index e436088..f2a76e2 100644
--- a/tests/cases/FenomTest.php
+++ b/tests/cases/FenomTest.php
@@ -10,7 +10,7 @@ class FenomTest extends \Fenom\TestCase
{
return array(
array("disable_methods", Fenom::DENY_METHODS),
- array("disable_native_funcs", Fenom::DENY_INLINE_FUNCS),
+ array("disable_native_funcs", Fenom::DENY_NATIVE_FUNCS),
array("disable_cache", Fenom::DISABLE_CACHE),
array("force_compile", Fenom::FORCE_COMPILE),
array("auto_reload", Fenom::AUTO_RELOAD),
@@ -20,6 +20,18 @@ class FenomTest extends \Fenom\TestCase
);
}
+ public function testCreating() {
+ $time = $this->tpl('temp.tpl', 'Template 1 a');
+ $fenom = new Fenom($provider = new \Fenom\Provider(FENOM_RESOURCES . '/template'));
+ $fenom->setCompileDir(FENOM_RESOURCES . '/compile');
+ $this->assertInstanceOf('Fenom\Template', $tpl = $fenom->getTemplate('temp.tpl'));
+ $this->assertSame($provider, $tpl->getProvider());
+ $this->assertSame('temp.tpl', $tpl->getBaseName());
+ $this->assertSame('temp.tpl', $tpl->getName());
+ $this->assertSame($time, $tpl->getTime());
+ $fenom->clearAllCompiles();
+ }
+
public function testCompileFile()
{
$a = array(
@@ -43,14 +55,19 @@ class FenomTest extends \Fenom\TestCase
$this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array()));
}
+ /**
+ * @group testCheckMTime
+ */
public function testCheckMTime()
{
$this->fenom->setOptions(Fenom::FORCE_COMPILE);
$this->tpl('custom.tpl', 'Custom template');
$this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array()));
-
- sleep(1);
+ $tpl = $this->fenom->getTemplate('custom.tpl');
+ $this->assertTrue($tpl->isValid());
+ usleep(1.5e6);
$this->tpl('custom.tpl', 'Custom template (new)');
+ $this->assertFalse($tpl->isValid());
$this->assertSame("Custom template (new)", $this->fenom->fetch('custom.tpl', array()));
}
@@ -133,4 +150,45 @@ class FenomTest extends \Fenom\TestCase
$this->assertSame('+++ |--- == hello ---||--- world == ---| +++', $this->fenom->compileCode('hello {var $user} god {/var} world')->fetch(array()));
}
-}
\ No newline at end of file
+
+ public function testAddInlineCompilerSmart() {
+ $this->fenom->addCompilerSmart('SayA','TestTags');
+ $this->tpl('inline_compiler.tpl', 'I just {SayA}.');
+ $this->assertSame('I just Say A.', $this->fenom->fetch('inline_compiler.tpl', array()));
+ }
+
+ public function testAddBlockCompilerSmart() {
+ $this->fenom->addBlockCompilerSmart('SayBlock', 'TestTags', array('SaySomething'), array('SaySomething'));
+ $this->tpl('block_compiler.tpl', '{SayBlock} and {SaySomething}. It is all, {/SayBlock}');
+ $this->assertSame('Start saying and say blah-blah-blah. It is all, Stop saying',
+ $this->fenom->fetch('block_compiler.tpl', array()));
+ }
+
+ public function testAddFunctions() {
+ $this->fenom->setOptions(Fenom::DENY_NATIVE_FUNCS);
+ $this->assertFalse($this->fenom->isAllowedFunction('substr'));
+ $this->fenom->addAllowedFunctions(array('substr'));
+ $this->assertTrue($this->fenom->isAllowedFunction('substr'));
+ }
+}
+
+
+
+class TestTags {
+
+ public static function tagSayA() {
+ return 'echo "Say A"';
+ }
+
+ public static function SayBlockOpen() {
+ return 'echo "Start saying"';
+ }
+
+ public static function tagSaySomething() {
+ return 'echo "say blah-blah-blah"';
+ }
+
+ public static function SayBlockClose() {
+ return 'echo "Stop saying"';
+ }
+}