Initial revision origin yatt-1
authorRex Feany <rfeany@rf.cx>
Mon, 30 Aug 2004 04:03:48 +0000 (04:03 +0000)
committerRex Feany <rfeany@rf.cx>
Mon, 30 Aug 2004 04:03:48 +0000 (04:03 +0000)
php/YATT.class.php [new file with mode: 0644]
php/example.php [new file with mode: 0644]
php/example.yatt [new file with mode: 0644]

diff --git a/php/YATT.class.php b/php/YATT.class.php
new file mode 100644 (file)
index 0000000..c6c9ff9
--- /dev/null
@@ -0,0 +1,215 @@
+<?
+/*
+ * Simple text template class. Does not handle caching.
+ *
+ * Version 1.1 
+ *
+ */
+
+# here is whats in each array node
+define('YATT_NAME',     0);  # node name
+define('YATT_OUTPUT',   1);  # output buffer for this node
+define('YATT_CONTEXTS', 2);  # not impl. in php: node context
+define('YATT_DSTART',   3);  # start of data/chilren nodes
+
+# This sucks, but array_pop doesn't deal well with references
+function &my_pop(&$array) {
+    if (!count($array)) return null;
+    end($array);
+    $var =& $array[key($array)];
+    array_pop($array);
+    return $var;
+}
+
+class YATT {
+
+    # Holds the template tree
+    var $obj;
+
+    # Holds variables
+    var $vars;
+
+    # Holds information on errors
+    var $errors;
+
+    # INTERNAL: Push an error onto the error stack!
+    function error() {
+        $args = func_get_args ();
+        $format = array_shift($args);
+        array_push($this->errors, vsprintf($format, $args));
+        return count($this->errors);
+    }
+
+    # INTERNAL: act as a callback for preg_replace_callback below
+    function pp_callback($matches) {
+        return $this->preprocess($matches[1]);
+    }
+
+    # INTERNAL: read file, preprocess for includes, strip out comments
+    function preprocess($fname) {
+        if (! ($data = file_get_contents($fname))) {
+            $this->error('INCLUDE(%s): can not open file!', $fname);
+            return '';
+        }
+
+        # strip all comments
+        $data = preg_replace('/[ \t]*\%\[#\].*$/m', '', $data);
+
+        # fetch all includes (recursive!)
+        return preg_replace_callback(
+                    '/^[ \t]*\%include[ \t]+\[([A-Za-z-_.]+)\][ \t]*$/im',
+                    array(&$this, 'pp_callback'),
+                    $data);
+    }
+
+    # load template file on class creation 
+    # You can load multiple files
+    function load($fname) {
+        $data = $this->preprocess($fname);
+        $stack = array();
+
+        $cur = &$this->obj;
+        $nchunks = preg_match_all(
+                '/(.*?)[ \t]*\%(begin|end)[\t ]+\[([A-Za-z-_]+)\][\t ]*$/ism', 
+                $data, $matches, PREG_SET_ORDER);
+
+        # array[1] == text, array[2] == (begin|end), array[3] == NAME
+        for ($i = 0; $i < $nchunks; $i++) {
+            $text = $matches[$i][1];
+            $type = strtolower($matches[$i][2]);
+            $name = $matches[$i][3];
+
+            if ($text && (strlen($text) > 0)) {
+                array_push($cur, $text);
+            }
+
+            if (strcasecmp($type, 'begin') == 0) {
+                $new = array($name, '', '');
+                $cur[] =& $new;
+                $stack[] =& $cur;
+                $cur = &$new;
+                unset($new);
+            } else if (strcasecmp($type, 'end') == 0) {
+                if (strcmp($cur[YATT_NAME], $name)) {
+                    return $this->error('LOAD(%s): Mismatched begin/end: got %s, wanted %s! aborting!',
+                                                $fname, $name, $cur[YATT_NAME]);
+                }
+                if (! ($cur =& my_pop($stack))) {
+                    $cur = &$this->obj;
+                }
+            } else {
+                return $this->error('LOAD(%s): unknown tag type %s, aborting!', $fname, $type);
+            }
+        }
+        if (count($stack)) {
+            return $this->error('LOAD(%s): mismatched begin/end pairs at EOF!', $fname);
+        }
+        return count($this->errors);
+    }
+
+    # INTERNAL: Find the node that corrosponds to an OID
+    function &find_node($path) {
+        $oid = explode('.', $path);
+        $node = &$this->obj;
+
+        while ($cmp = array_shift($oid)) {
+            $old = &$node;
+            for ($i = YATT_DSTART; $i < count($node); $i++) {
+                if (is_array($node[$i]) && (strcmp($node[$i][YATT_NAME], $cmp) == 0)) {
+                        $node = &$node[$i];
+                        break;
+                }
+            }
+            if ($old == $node) {
+                $this->error('FIND(%s): Could not find node %s', $path, $cmp);
+                return FALSE;
+            }
+        }
+        return $node;
+    }
+
+    # INTERNAL: Substitute some stuff! 
+    function subst($matches) {
+        if (!isset($this->vars[$matches[1]])) {
+           $this->error('PARSE(): unbound variable %s', $matches[1]);
+           return '';
+        }
+        return $this->vars[$matches[1]];
+    }
+
+    # INTERNAL: Build the output for this node
+    function build_output(&$root, &$node) {
+        $out = '';
+
+        for ($i = YATT_DSTART; $i < count($node); $i++) {
+            if (is_array($node[$i])) {
+                $out .= $this->return_output($node[$i]);
+            } else {
+                $buf = $node[$i];
+                $pass = 0;
+
+                while (preg_match('/\%\[([^][%]+)\]/', $buf)) {
+                        if ($pass++ > 10) {
+                            # TODO: give the user more info when this happens.
+                            $this->error('PARSE(): recursive subst in node %s?', $node[YATT_NAME]);
+                            break;
+                        }
+                        $buf = preg_replace_callback('/\%\[([^][%]+)\]/', array(&$root, 'subst'), $buf);
+                }
+                $out .= $buf;
+            }
+        }
+        return $out;
+    }
+    # INTERNAL: Return all of the generated output
+    function return_output(&$node) {
+        $out = $node[YATT_OUTPUT];
+        $node[YATT_OUTPUT] = '';
+
+        for ($i = YATT_DSTART; $i < count($node); $i++) {
+            if (is_array($node[$i])) {
+                $out .= $this->return_output($node[$i]);
+            }
+        }
+        return $out;
+    }
+
+    # Create a new YATT instance.
+    function YATT() {
+        $this->errors = array();
+        $this->obj = array('ROOT', '', '');
+    }
+
+    # Return output, starting at a given node
+    function output($path=NULL) {
+        $obj =& $this->find_node($path);
+        return $obj ? $this->return_output($obj) : FALSE;
+    }
+
+    # Generate text from an object tree
+    function parse($path) {
+        if ($obj =& $this->find_node($path)) {
+            $obj[YATT_OUTPUT] .= $this->build_output($this, $obj);
+        }
+    }
+
+    # Set a variable to some value 
+    function set($var, $value=NULL) {
+        if (is_array($var)) {
+            $this->vars = array_merge($this->vars, $var);
+        } else {
+            $this->vars[$var] = $value;
+        }
+    }
+
+    # Get errors, or return FALSE if there are none. 
+    # Resets error list to zero!
+    function get_errors() {
+        $err = $this->errors;
+        $this->errors = array();
+        return count($err) ? $err : FALSE;
+    }
+}
+
+?>
diff --git a/php/example.php b/php/example.php
new file mode 100644 (file)
index 0000000..e4e8098
--- /dev/null
@@ -0,0 +1,56 @@
+<?
+
+require_once('YATT.class.php');
+
+$n = new YATT();
+
+$n->load("example.yatt");
+
+# set a variable
+$n->set('CRACK', 'eat me');
+
+# or like this
+$n->set(array("FOO" => "something else", "BAR" => "barorific!"));
+
+# Variable substitution is recursive, see how this works in the template
+$n->set('FNORK', 'baz');
+$n->set('CORK_baz', 'shout off and die');
+
+# Parse a single block of text
+$n->parse('faz.test');
+
+# Maybe build a table
+foreach (array("ONE", "TWO", "THREE") as $value) {
+        $n->set('ROW_NAME', $value);
+        $n->parse('faz.table.row');
+}
+$n->parse('faz.table');
+
+# Comment this out and look at the output.
+# Notice that everything *but* what is in faz is displayed.
+# Only nodes that you parse() actually display things.
+$n->parse('faz');
+
+# print the output for everything.
+# You could also tell it where to start, like output('faz.table')
+# to only print out stuff from table.
+print "--------------------------------------------------------------\n";
+print "Output of the template:\n";
+print "--------------------------------------------------------------\n";
+print $n->output();
+
+# If there were any errors, print them out
+# You should probably check this *before* you print out the page
+# because these errors will likely cause the page not to be
+# built correctly. It is done this way so that you only have to
+# check once, and so you can do something cool with the errors
+# instead of die().
+if (($e = $n->get_errors())) {
+    print "--------------------------------------------------------------\n";
+    print "errors:\n";
+    print "--------------------------------------------------------------\n";
+
+    print_r($e);
+}
+
+?>
diff --git a/php/example.yatt b/php/example.yatt
new file mode 100644 (file)
index 0000000..6a329b1
--- /dev/null
@@ -0,0 +1,49 @@
+%[#]
+%[#] This is a template comment
+%[#] This is stripped out by the template parser
+%[#]
+
+%[#] This is an include statement, it loads the file as if it were
+%[#] a part of this one. This should generate an error since missing.yatt
+%[#] isn't actually included with the example.
+%include [missing.yatt]
+
+%[#] This begins a block of text, blocks can be inside of other blocks.
+%begin [faz]
+
+Here is some static text to be printed when 'faz' is parsed.
+
+This is a variable: %[CRACK]
+
+%[#] Another random block of text: parsed with 'faz.test'
+%begin [test]
+
+recursive variable substitution in action:
+this is recursive: %[CORK_%[FNORK]] wheee!
+
+%end [test]
+
+
+some other stuff: %[FOO]
+some other stuff: %[BAR]
+
+undefined variables are bad: this should generate an error: %[UNDEFINED]
+
+%[#] How about another example?
+%begin [table]
+
+foo table header
+--------------
+%begin [row]
+FF: %[ROW_NAME]
+%end [row]
+--------------
+foo table footer
+
+%end [table]
+
+
+and some stuff down here!
+
+%end [faz]
+