diff --git a/README.textile b/README.textile index ccd74bdb..46528336 100644 --- a/README.textile +++ b/README.textile @@ -41,7 +41,7 @@ Example snippet: h2. Howto use Liquid -Code to render: +Code to render with original php-liquid:
 $liquid->parse(file_get_contents('templates/products.tpl'))->render($products);
@@ -61,3 +61,24 @@ h2. Fork Notes
 
 This fork is based on php-liquid by Mateo Murphy. The original library is still hosted at: ("http://code.google.com/p/php-liquid/":http://code.google.com/p/php-liquid/)
 
+Variables couldn't be used inside an 'include' or an 'extends' tag.
+
+Which means the below code didn't work:
+
+
+{% include foo %}
+{% include {{ foo }} %}
+
+ +With this modification it will work (where a string not inside a " or ' is considered as a variable): + +
+{% include merchant/'head' %}
+{% extends "root"/foo/bar/'index' %}
+
+ +Code to render with modified php-liquid (backward compatible, which means the original example does still work but variables can't be used in include and extends tags): + +
+$liquid->parse(file_get_contents('templates/products.tpl'), $products)->render();
+
diff --git a/lib/LiquidContext.class.php b/lib/LiquidContext.class.php index 6ba00265..264ad92c 100644 --- a/lib/LiquidContext.class.php +++ b/lib/LiquidContext.class.php @@ -68,6 +68,17 @@ public function addFilters($filter) } + /** + * Add registers to the context + * + * @param array $registers + */ + public function addRegisters($registers = array()) + { + $this->registers = $registers; + } + + /** * Invoke the filter that matches given name * diff --git a/lib/LiquidTemplate.class.php b/lib/LiquidTemplate.class.php index ee0eb970..3cf5b06d 100644 --- a/lib/LiquidTemplate.class.php +++ b/lib/LiquidTemplate.class.php @@ -39,6 +39,11 @@ class LiquidTemplate private static $_cache; + /** + * @var LiquidContext The context for keeping and resolving variables + */ + private static $_context; + /** * Constructor @@ -131,6 +136,17 @@ public static function getTags() } + /** + * + * + * @return array + */ + public static function getContext() + { + return self::$_context; + } + + /** * Register the filter * @@ -158,11 +174,14 @@ public static function tokenize($source) * Parses the given source string * * @param string $source + * @param array $assigns An array of values for the template */ - public function parse($source) + public function parse($source, array $assigns = array()) { $cache = self::$_cache; + self::$_context = new LiquidContext($assigns, null); + if (isset($cache)) { if (($this->_root = $cache->read(md5($source))) != false && $this->_root->checkIncludes() != true) @@ -192,7 +211,13 @@ public function parse($source) */ public function render(array $assigns = array(), $filters = null, $registers = null) { - $context = new LiquidContext($assigns, $registers); + $context = self::$_context; + if (!empty($assigns) && is_array($assigns)) { + $context->merge($assigns); + } + if (!is_null($registers) && is_array($registers)) { + $context->addRegisters($registers); + } if (!is_null($filters)) { @@ -211,6 +236,7 @@ public function render(array $assigns = array(), $filters = null, $registers = n $context->addFilters($filter); } + self::$_context = $context; return $this->_root->render($context); } } diff --git a/lib/Tag/LiquidTagExtends.class.php b/lib/Tag/LiquidTagExtends.class.php index fff9a923..827915a8 100644 --- a/lib/Tag/LiquidTagExtends.class.php +++ b/lib/Tag/LiquidTagExtends.class.php @@ -38,50 +38,72 @@ class LiquidTagExtends extends LiquidTag */ public function __construct($markup, &$tokens, &$fileSystem) { - $regex = new LiquidRegexp('/("[^"]+"|\'[^\']+\')?/'); + $regex = new LiquidRegexp('/([^\s]+)?/'); if ($regex->match($markup)) { - $this->_templateName = substr($regex->matches[1], 1, strlen($regex->matches[1]) - 2); + $regexTemplateName = new LiquidRegexp('/"[^"]+"|\'[^\']+\'|[^"\'\/]+/'); + if ($regexTemplateName->match_all($regex->matches[1])) { + $regexQuote = new LiquidRegexp('/"[^"]+"|\'[^\']+\'/'); + $context = LiquidTemplate::getContext(); + $templName = ""; + foreach ($regexTemplateName->matches[0] as $templPart) { + $templPartName = ''; + if ($regexQuote->match($templPart)) { + $templPartName = trim($templPart,"\"''"); + } else { + $templPartName = $context->get($templPart); + } + if (!empty($templPartName)) { + if (!empty($templName)) $templName .= "/"; + $templName .= $templPartName; + } + } + $this->_templateName = $templName; + } + + if (empty($this->_templateName)) { + throw new LiquidException("Error in tag 'extends' - Valid syntax: include [template]|'[template]' (with|for) [object|collection]"); + } } else { - throw new LiquidException("Error in tag 'extends' - Valid syntax: extends '[template name]'"); + throw new LiquidException("Error in tag 'extends' - Valid syntax: extends [template]|'[template name]'"); } parent::__construct($markup, $tokens, $fileSystem); } - private function _findBlocks($tokens) - { - $blockstartRegexp = new LiquidRegexp('/^' . LIQUID_TAG_START . '\s*block (\w+)\s*(.*)?' . LIQUID_TAG_END . '$/'); - $blockendRegexp = new LiquidRegexp('/^' . LIQUID_TAG_START . '\s*endblock\s*?' . LIQUID_TAG_END . '$/'); - - $b = array(); - $name = null; - - foreach($tokens as $token) - { - if($blockstartRegexp->match($token)) - { - $name = $blockstartRegexp->matches[1]; - $b[$name] = array(); - } - else if($blockendRegexp->match($token)) - { - $name = null; - } - else - { - if(isset($name)) - { - array_push($b[$name], $token); - } - } - } - - return $b; + private function _findBlocks($tokens) + { + $blockstartRegexp = new LiquidRegexp('/^' . LIQUID_TAG_START . '\s*block (\w+)\s*(.*)?' . LIQUID_TAG_END . '$/'); + $blockendRegexp = new LiquidRegexp('/^' . LIQUID_TAG_START . '\s*endblock\s*?' . LIQUID_TAG_END . '$/'); + + $b = array(); + $name = null; + + foreach($tokens as $token) + { + if($blockstartRegexp->match($token)) + { + $name = $blockstartRegexp->matches[1]; + $b[$name] = array(); + } + else if($blockendRegexp->match($token)) + { + $name = null; + } + else + { + if(isset($name)) + { + array_push($b[$name], $token); + } + } + } + + return $b; } @@ -120,10 +142,10 @@ public function parse(&$tokens) $childtokens = $this->_findBlocks($tokens); $blockstartRegexp = new LiquidRegexp('/^' . LIQUID_TAG_START . '\s*block (\w+)\s*(.*)?' . LIQUID_TAG_END . '$/'); - $blockendRegexp = new LiquidRegexp('/^' . LIQUID_TAG_START . '\s*endblock\s*?' . LIQUID_TAG_END . '$/'); - - $b = array(); - $name = null; + $blockendRegexp = new LiquidRegexp('/^' . LIQUID_TAG_START . '\s*endblock\s*?' . LIQUID_TAG_END . '$/'); + + $b = array(); + $name = null; $rest = array(); $aufzeichnen = false; @@ -131,7 +153,7 @@ public function parse(&$tokens) for($i = 0; $i < count($maintokens); $i++) { if($blockstartRegexp->match($maintokens[$i])) - { + { $name = $blockstartRegexp->matches[1]; if(isset($childtokens[$name])) @@ -141,16 +163,16 @@ public function parse(&$tokens) foreach($childtokens[$name] as $item) array_push($rest, $item); } - - } + + } if(!$aufzeichnen) - array_push($rest, $maintokens[$i]); + array_push($rest, $maintokens[$i]); - if($blockendRegexp->match($maintokens[$i]) && $aufzeichnen === true) - { + if($blockendRegexp->match($maintokens[$i]) && $aufzeichnen === true) + { $aufzeichnen = false; array_push($rest, $maintokens[$i]); - } + } } } diff --git a/lib/Tag/LiquidTagInclude.class.php b/lib/Tag/LiquidTagInclude.class.php index b2760808..e5a72133 100644 --- a/lib/Tag/LiquidTagInclude.class.php +++ b/lib/Tag/LiquidTagInclude.class.php @@ -45,8 +45,8 @@ class LiquidTagInclude extends LiquidTag */ private $_document; - /** - * @var string The Source Hash + /** + * @var string The Source Hash */ protected $_hash; @@ -61,12 +61,33 @@ class LiquidTagInclude extends LiquidTag */ public function __construct($markup, &$tokens, &$fileSystem) { - $regex = new LiquidRegexp('/("[^"]+"|\'[^\']+\')(\s+(with|for)\s+(' . LIQUID_QUOTED_FRAGMENT . '+))?/'); + $regex = new LiquidRegexp('/([^\s]+)(\s+(with|for)\s+(' . LIQUID_QUOTED_FRAGMENT . '+))?/'); if ($regex->match($markup)) { + $regexTemplateName = new LiquidRegexp('/"[^"]+"|\'[^\']+\'|[^"\'\/]+/'); + if ($regexTemplateName->match_all($regex->matches[1])) { + $regexQuote = new LiquidRegexp('/"[^"]+"|\'[^\']+\'/'); + $context = LiquidTemplate::getContext(); + $templName = ""; + foreach ($regexTemplateName->matches[0] as $templPart) { + $templPartName = ''; + if ($regexQuote->match($templPart)) { + $templPartName = trim($templPart,"\"''"); + } else { + $templPartName = $context->get($templPart); + } + if (!empty($templPartName)) { + if (!empty($templName)) $templName .= "/"; + $templName .= $templPartName; + } + } + $this->_templateName = $templName; + } - $this->_templateName = substr($regex->matches[1], 1, strlen($regex->matches[1]) - 2); + if (empty($this->_templateName)) { + throw new LiquidException("Error in tag 'include' - Valid syntax: include [template]|'[template]' (with|for) [object|collection]"); + } if (isset($regex->matches[1])) { @@ -78,7 +99,7 @@ public function __construct($markup, &$tokens, &$fileSystem) } else { - throw new LiquidException("Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"); + throw new LiquidException("Error in tag 'include' - Valid syntax: include [template]|'[template]' (with|for) [object|collection]"); } parent::__construct($markup, $tokens, $fileSystem); diff --git a/test/liquid/StandardTagTest.php b/test/liquid/StandardTagTest.php index a28d3442..08824bf5 100644 --- a/test/liquid/StandardTagTest.php +++ b/test/liquid/StandardTagTest.php @@ -20,8 +20,23 @@ function readTemplateFile($templatePath) { if ($templatePath == 'inner') { return "Inner: {{ inner }}{{ other }}"; - - } + } + + if ($templatePath == 'inner2') { + return "Inner: {{ inner }}{{ other }}"; + } + + if ($templatePath == 'prob/foo/bar/prob/inner2') { + return "Inner in prob/foo/bar/prob/: {{ inner }}{{ other }}"; + } + + if ($templatePath == 'outer') { + return "Outer: {% block title %}outer-title{% endblock %}"; + } + + if ($templatePath == 'prob/foo/bar/prob/outer') { + return "Outer in prob/foo/bar/prob: {% block title %}outer-title{% endblock %}"; + } } } @@ -344,4 +359,44 @@ function test_include_tag_no_with() $this->assertEqual("Outer-Inner: orig-Outer-Inner: orig23", $output); } + function test_include_tag_with_variable() + { + $template = new LiquidTemplate(); + $template->setFileSystem(new LiquidTestFileSystem()); + + $template->parse("Outer-{% include inner2 %}-Outer with variable-{% include inner2 other:'23' %}", array("inner2"=>"inner2", "inner"=>"orig", "var" => array(1,2,3))); + $output = $template->render(); + $this->assertEqual("Outer-Inner: orig-Outer with variable-Inner: orig23", $output); + + $template->parse("Outer-{% include 'prob'/foo/bar/\"prob\"/inner2 %}-Outer with variable-{% include 'prob'/foo/bar/\"prob\"/inner2 other:'23' %}", array("inner2"=>"inner2", "foo"=>"foo", "bar"=>"bar", "inner"=>"orig", "var" => array(1,2,3))); + $output = $template->render(); + $this->assertEqual("Outer-Inner in prob/foo/bar/prob/: orig-Outer with variable-Inner in prob/foo/bar/prob/: orig23", $output); + } + + function test_extends_tag() + { + $template = new LiquidTemplate(); + $template->setFileSystem(new LiquidTestFileSystem()); + + $template->parse("{% extends 'outer' %} {% block title %}inner-title{% endblock %}"); + + $output = $template->render(); + + $this->assertEqual("Outer: inner-title", $output); + } + + function test_extends_tag_with_variable() + { + $template = new LiquidTemplate(); + $template->setFileSystem(new LiquidTestFileSystem()); + + $template->parse("{% extends outer %} {% block title %}inner-title with variable{% endblock %}", array("outer"=>"outer")); + $output = $template->render(); + $this->assertEqual("Outer: inner-title with variable", $output); + + $template->parse("{% extends 'prob'/foo/bar/\"prob\"/outer %} {% block title %}inner-title with variable{% endblock %}", array("outer"=>"outer", "foo"=>"foo", "bar"=>"bar")); + $output = $template->render(); + $this->assertEqual("Outer in prob/foo/bar/prob: inner-title with variable", $output); + } + } \ No newline at end of file