Permalink
Browse files

Added a new feature called recursive callbacks and upgraded README

  • Loading branch information...
ziggys committed Dec 12, 2011
1 parent a024188 commit e998beeb12e26d14fe95622baa7ead8cf83a54d0
Showing with 155 additions and 5 deletions.
  1. +77 −0 README.md
  2. +78 −5 lib/Lex/Parser.php
View
@@ -185,6 +185,7 @@ In the following example, let's assume you have the following array/object of va
'name' => 'Lex',
'contributors' => array(
array('name' => 'Dan'),
+ array('name' => 'Ziggy')
),
),
),
@@ -287,3 +288,79 @@ The callback must also return a string, which will replace the tag in the conten
// Do something useful
return $result;
}
+
+Recursive Callback Blocks
+-------------
+
+The recursive callback tag allows you to loop through a childs element with the same output as the block.
+
+**Example**
+
+ function my_callback($name, $attributes, $content)
+ {
+ $data = array(
+ 'url' => 'url_1',
+ 'title' => 'First Title',
+ 'children' => array(
+ array(
+ 'url' => 'url_2',
+ 'title' => 'Second Title',
+ 'children' => array(
+ array(
+ 'url' => 'url_3',
+ 'title' => 'Third Title'
+ )
+ )
+ ),
+ array(
+ 'url' => 'url_4',
+ 'title' => 'Fourth Title',
+ 'children' => array(
+ array(
+ 'url' => 'url_5',
+ 'title' => 'Fifth Title'
+ )
+ )
+ )
+ )
+ );
+
+ $parser = new Lex_Parser();
+ return $parser->parse($content, $data);
+ }
+
+
+In the template set it up as follows.
+
+ <ul>
+ {{ navigation }}
+ <li><a href="{{ url }}">{{ title }}</a>
+ {{ if children }}
+ <ul>
+ {{ *recursive children* }}
+ </ul>
+ {{ endif }}
+ </li>
+ {{ /navigation }}
+ </ul>
+
+
+**Result**
+
+ <ul>
+ <li><a href="url_1">First Title</a>
+ <ul>
+ <li><a href="url_2">Second Title</a>
+ <ul>
+ <li><a href="url_3">Third Title</a></li>
+ </ul>
+ </li>
+
+ <li><a href="url_4">Fourth Title</a>
+ <ul>
+ <li><a href="url_5">Fifth Title</a></li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+ </ul>
View
@@ -11,6 +11,7 @@ class LexParsingException extends Exception { }
class Lex_Parser
{
+ protected $allow_php = false;
protected $regex_setup = false;
protected $scope_glue = '.';
protected $tag_regex = '';
@@ -51,7 +52,8 @@ class Lex_Parser
public function parse($text, $data = array(), $callback = false, $allow_php = false)
{
$this->setup_regex();
-
+ $this->allow_php = $allow_php;
+
// Is this the first time parse() is called?
if (Lex_Parser::$data === null)
{
@@ -150,6 +152,7 @@ public function parse_variables($text, $data, $callback = null)
{
$str = $this->parse_callback_tags($str, $item_data, $callback);
}
+
$looped_text .= $str;
}
$text = preg_replace('/'.preg_quote($match[0][0], '/').'/m', addcslashes($looped_text, '\\$'), $text, 1);
@@ -218,14 +221,18 @@ public function parse_callback_tags($text, $data, $callback)
$name = $match[1][0];
if (isset($match[2]))
{
+ $cb_data = $data;
+ if ( !empty(Lex_Parser::$callback_data))
+ {
+ $cb_data = array_merge(Lex_Parser::$callback_data, $data);
+ }
$raw_params = $this->inject_extractions($match[2][0], '__cond_str');
- $parameters = $this->parse_parameters($raw_params, array_merge($data, Lex_Parser::$callback_data), $callback);
+ $parameters = $this->parse_parameters($raw_params, $cb_data, $callback);
}
$content = '';
$temp_text = substr($text, $start + strlen($tag));
-
if (preg_match('/\{\{\s*\/'.preg_quote($name, '/').'\s*\}\}/m', $temp_text, $match, PREG_OFFSET_CAPTURE))
{
$content = substr($temp_text, 0, $match[0][1]);
@@ -240,15 +247,15 @@ public function parse_callback_tags($text, $data, $callback)
}
$replacement = call_user_func_array($callback, array($name, $parameters, $content));
-
+ $replacement = $this->parse_recursives($replacement, $content, $callback);
+
if ($in_condition)
{
$replacement = $this->value_to_literal($replacement);
}
$text = preg_replace('/'.preg_quote($tag, '/').'/m', addcslashes($replacement, '\\$'), $text, 1);
$text = $this->inject_extractions($text, 'nested_looped_tags');
}
-
return $text;
}
@@ -311,6 +318,70 @@ public function parse_conditionals($text, $data, $callback)
return $text;
}
+ /**
+ * Goes recursively through a callback tag with a passed child array.
+ *
+ * @param string $text - The replaced text after a callback.
+ * @param string $orig_text - The original text, before a callback is called.
+ * @param mixed $callback
+ * @return string $text
+ */
+ public function parse_recursives($text, $orig_text, $callback)
+ {
+ // Is there a {{ *recursive [array_key]* }} tag here, let's loop through it.
+ if (preg_match($this->recursive_regex, $text, $match))
+ {
+ $array_key = $match[1];
+ $tag = $match[0];
+ $next_tag = null;
+ $children = Lex_Parser::$callback_data[$array_key];
+ $child_count = count($children);
+ $count = 1;
+
+ // Is the array not multi-dimensional? Let's make it multi-dimensional.
+ if ($child_count == count($children, COUNT_RECURSIVE))
+ {
+ $children = array($children);
+ $child_count = 1;
+ }
+
+ foreach ($children as $child)
+ {
+ $has_children = true;
+
+ // If this is a object let's convert it to an array.
+ is_array($child) OR $child = (array) $child;
+
+ // Does this child not contain any children?
+ // Let's set it as empty then to avoid any errors.
+ if ( ! array_key_exists($array_key, $child))
+ {
+ $child[$array_key] = array();
+ $has_children = false;
+ }
+
+ $replacement = $this->parse($orig_text, $child, $callback, $this->allow_php);
+
+ // If this is the first loop we'll use $tag as reference, if not
+ // we'll use the previous tag ($next_tag)
+ $current_tag = ($next_tag !== null) ? $next_tag : $tag;
+
+ // If this is the last loop set the next tag to be empty
+ // otherwise hash it.
+ $next_tag = ($count == $child_count) ? '' : md5($tag.$replacement);
+
+ $text = str_replace($current_tag, $replacement.$next_tag, $text);
+
+ if ($has_children)
+ {
+ $text = $this->parse_recursives($text, $orig_text, $callback);
+ }
+ $count++;
+ }
+ }
+ return $text;
+ }
+
/**
* Gets or sets the Scope Glue
*
@@ -466,6 +537,8 @@ protected function setup_regex()
$this->callback_block_regex = '/\{\{\s*('.$this->variable_regex.')(\s.*?)\}\}(.*?)\{\{\s*\/\1\s*\}\}/ms';
+ $this->recursive_regex = '/\{\{\s*\*recursive\s*('.$this->variable_regex.')\*\s*\}\}/ms';
+
$this->noparse_regex = '/\{\{\s*noparse\s*\}\}(.*?)\{\{\s*\/noparse\s*\}\}/ms';
$this->conditional_regex = '/\{\{\s*(if|elseif)\s*((?:\()?(.*?)(?:\))?)\s*\}\}/ms';

0 comments on commit e998bee

Please sign in to comment.