From: Josh Grams Date: Tue, 4 Aug 2009 18:29:39 +0000 (-0400) Subject: * template.php: changed arg order, new context stack implementation. X-Git-Url: https://jasonwoof.com/gitweb/?p=wfpl.git;a=commitdiff_plain;h=ea7f4763f4ee51f22091aa6e75cd4c59cea55bc0 * template.php: changed arg order, new context stack implementation. --- diff --git a/run.php b/run.php index 26a8b36..4e56e98 100644 --- a/run.php +++ b/run.php @@ -137,7 +137,7 @@ function run_php($dest = false) { } elseif($html_exists) { $template = parse_template_file("$html_file"); } - if($template) print fill_template($data, $template); + if($template) print fill_template($template, $data); } run_php(); diff --git a/template.php b/template.php index da05621..0e1688c 100644 --- a/template.php +++ b/template.php @@ -45,12 +45,12 @@ require_once('code/wfpl/misc.php'); # Public functions # ---------------- -function template($data, $template) { - return fill_template($data, parse_template($template)); +function template($template, $data) { + return fill_template(parse_template($template), $data); } -function template_file($data, $filename) { - return fill_template($data, parse_template_file($filename)); +function template_file($filename, $data) { + return fill_template(parse_template_file($filename), $data); } function parse_template_file($filename) { @@ -98,32 +98,143 @@ function parse_template($string) { return $tem; } -# Then we do a depth-first traversal of the template tree, -# replacing all tags with the data values. - -function fill_template($data, $template, $context = NULL, $keychain = NULL) { - $context[] = $data; - foreach($template['pieces'] as $piece) { - if(is_string($piece)) $output .= $piece; - else { - if($piece['pieces']) { # sub-template - $keychain[] = $piece['name']; - $rows = tem_get_rows($piece, $context, $keychain); - foreach($rows as $key => $row) { - $keychain[] = $key; - $output .= fill_template($row, $piece, $context, $keychain); - array_pop($keychain); - } - array_pop($keychain); - } else { # variable - $output .= tem_get_enc($piece, $context); +# To fill out a template, we do a depth-first traversal of the template +# tree, replacing all tags with the data values. + +# The data starts out as a nested set of key/value pairs, where the +# values can be: + + # a string to fill a value slot + # a hash to fill one instance of a sub-template + # an array of hashes to fill multiple instances of a sub-template + +# The middle form will be converted to the last form as we use it. + +function tem_data_as_rows($value) { + if(is_array($value)) { + # numeric keys, is already array of arrays -- expand sub-template for each. + if(array_key_exists(0, $value)) return $value; + # key/value pairs -- expand sub-template once. + else return array($value); + } elseif($value) { + # value -- expand sub-template once using only parent values + return array(array()); + } else { + # empty value -- don't expand sub-template + return array(); + } +} + + + +# We use dynamic scoping, so we keep a stack of namespaces. +# Each scope references a hash and the parent scope. + +function &tem_scope(&$data, $push = true) { + static $stack = array(); + + if($push) { + $parent =& last($stack); + $stack[] = array(); + + $scope =& last($stack); + $scope['data'] =& $data; + if($parent) $scope['parent'] =& $parent; + } else { # pop + array_pop($stack); + } + return last($stack); +} + +function &tem_end_scope() { return tem_scope($x, false); } + +# To look up a key, we check each namespace (starting with the +# innermost one) until a value is found. + +function tem_find_scope($key, $context) { + $scope = $context; + do{ + if(array_key_exists($key, $scope['data'])) { + return $scope; + } + } while($scope = $scope['parent']); + + # not found; return empty scope. + return array('parent' => $context); +} + +function tem_get($key, $context) { + $scope = tem_find_scope($key, $context); + if($scope) return $scope['data'][$key]; +} + +# Return the value for a tag as a set of rows to fill a sub-template. +# If $tag has an arg, call the tem_auto function to munge the data. +function &tem_get_rows($tag, $context) +{ + $key = $tag['name']; + if(count($tag['args'])) { + $func = "tem_auto_" . $tag['args'][0]; + function_exists($func) + or die("ERROR: template auto function '$func' not found.
\n"); + } + $scope = tem_find_scope($key, $context); + + if($func) $value = $func($key, $scope); + else $value = $scope['data'][$key]; + + $rows = tem_data_as_rows($value); + if(is_array($value)) $scope['data'][$key] = $rows; + + return $rows; +} + +# Return the value for a tag as an encoded string. +function tem_get_enc($tag, $context) +{ + $key = $tag['name']; + $value = tem_get($key, $context); + foreach($tag['args'] as $encoding) { + $func = "enc_$encoding"; + if(function_exists($func)) $value = $func($value, $key); + else die("ERROR: encoder function '$func' not found.
\n"); + } + return $value; +} + +function fill_template($template, &$data, &$context = NULL) { + if(!$context) $context =& tem_scope($data); + foreach($template['pieces'] as $tem) { + if(is_string($tem)) $output .= $tem; + elseif($tem['pieces']) { # sub-template + $rows =& tem_get_rows($tem, $context); + foreach($rows as $key => &$row) { + $context =& tem_scope($row); + $context['rows'] =& $rows; + $output .= fill_template($tem, $row, $context); + $context =& tem_end_scope(); + } + } else { # variable + $output .= tem_get_enc($tem, $context); } } return $output; } + +# Return a hash containing the top-level sub-templates of tem. +function top_sub_templates($tem) { + $subs = array(); + foreach($tem['pieces'] as $piece) { + if(is_array($piece) and $piece['pieces']) { + $subs[$piece['name']] = $piece; + } + } + return $subs; +} + # Replace top-level values in $main with top-level templates from $tem. function merge_templates($main, $tem) { $out = array('name' => $main['name'], 'pieces' => array()); @@ -156,102 +267,32 @@ function merge_templates($main, $tem) { # # # -function tem_auto_sep($piece, $context, $keychain) { - list($name, $index, $this_name) = array_slice($keychain, -3); - $array = tem_get($name, $context); - if($index != count($array)-1) return true; +function tem_auto_sep($key, $context) { + $rows =& $context['parent']['rows']; + if(key($rows)) return true; + # else we are on the last row (cursor has hit the end and reset). } # 'once' sections will be shown once unless the corresponding data value # is false. We check only for false; 0 or '' will not work. -function tem_auto_once($piece, $context, $keychain) { - $value = tem_get(array_pop($keychain), $context); - if($value !== false) return true; +function tem_auto_once($key, $context) { + if($context['data'][$key] !== false) + return tem_data_as_rows(true); } # 'evenodd' sections are given an 'evenodd' attribute whose value # alternates between 'even' and 'odd'. -function tem_auto_evenodd($piece, $context, $keychain) { - $rows = tem_get(array_pop($keychain), $context); +function tem_auto_evenodd($key, $context) { + $rows = $context['data'][$key]; $even = 0; $text = array('even', 'odd'); foreach($rows as $key => $value) { $rows[$key]['evenodd'] = $text[$even]; $even = 1 - $even; } - return $rows; -} - - - -# Internal functions -# ------------------ -# -# Of course, nothing stops you from using these, but I don't know -# why you would want to... - - -# Convert value to array of hashes for use in sub-template expansion. -# This adds flexibility to how you represent your data. -function tem_value_as_rows($value) { - if(is_array($value)) { - # numeric keys, is already array of arrays -- expand sub-template for each. - if(array_key_exists(0, $value)) return $value; - # key/value pairs -- expand sub-template once. - else return array($value); - } elseif($value) { - # value -- expand sub-template once using only parent values - return array(array()); - } else { - # empty value -- don't expand sub-template - return array(); - } -} - -# Search the current context and return the value for key. -function tem_get($key, $context) { - while($context) { - $data = array_pop($context); - if(array_key_exists($key, $data)) return $data[$key]; - } -} - -# Return the value for a tag as a set of rows to fill a sub-template. -# If $tag has an arg, call the tem_auto function to munge the data. -function tem_get_rows($tag, $context, $keychain) -{ - if(count($tag['args'])) { - $func = "tem_auto_" . $tag['args'][0]; - if(function_exists($func)) $value = $func($tag, $context, $keychain); - else die("ERROR: template auto function '$func' not found.
\n"); - } else $value = tem_get($tag['name'], $context); - - return tem_value_as_rows($value); -} - -# Return the value for a tag as an encoded string. -function tem_get_enc($tag, $context) -{ - $key = $tag['name']; - $value = tem_get($key, $context); - foreach($tag['args'] as $encoding) { - $func = "enc_$encoding"; - if(function_exists($func)) $value = $func($value, $key); - else die("ERROR: encoder function '$func' not found.
\n"); - } - return $value; -} - -function top_sub_templates($tem) { - $subs = array(); - foreach($tem['pieces'] as $piece) { - if(is_array($piece) and $piece['pieces']) { - $subs[$piece['name']] = $piece; - } - } - return $subs; + return tem_data_as_rows($rows); } ?>