Location: PHPKode > scripts > Simple Template Parser > simple-template-parser/stp.php
<?
# parser.inc
// Siple Template Parser is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the License,
// or (at your option) any later version.
// PixHound is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston,
// MA  02111-1307  USA
//
// © 2005 Stefan Reich / Tobi Schulz
// http://www.script.gr/scripts/STP/

class Parser2 {
  # Do NOT call variables from the outside ! (Use the public methods instead.)
  var $params = array();
  var $paramObject;
  var $template;
  var $output; // NULL with parseAndEcho, Array otherwise
  var $includePath;

  ########################################################
  ################## Internal Functions ##################
  ########################################################
  
  ## checkCondition()
  ## Function to check for conditions in IF Tags
  ## Possible conditions:
  #
  #  gt = greater (Numbers only)
  #  ge = greater or equal (Numbers only)
  #  lt = lower (Numbers only)
  #  le = lower or equal (Numbers only)
  #  eq = equal (Number or string)
  #  ne = not equal (Number or string)
  #  lk = existis in string (String only, functions like the 
  #       SQL LIKE '%string%'
  #
  # $value = value to check condition against
  #
  # 
  
  function checkCondition($value, $condition) {
    ereg("([^ ]+) (.+)",$condition,$cond);
    $chh = $cond[1];
    $wert = $cond[2];
    $wert = ereg_replace("['|\"]","",$wert);
    if($chh == "gt") return ($value >  $wert);
    if($chh == "ge") return ($value >= $wert);
    if($chh == "lt") return ($value <  $wert);
    if($chh == "le") return ($value <= $wert);
    if($chh == "eq") return ($value == $wert);
    if($chh == "ne") return ($value != $wert);
    if($chh == "lk") return eregi($wert,$value);
    return $wert;
  }

  ## colorSet(string $colorstring)
  ## Enables changing Colors f.e. in table rows
  ## initiating an array with the color values
  ## as given by the template in form
  ## <#ATTR COLOR1,COLOR2,COLOR3...#> 
  ## 
  #
  function colorSet($colorstring)  {
    $colorstring = ereg_replace(" +","",$colorstring);
    $this->colors = split(",", $colorstring);
    $this->colorindex = 0;
    }
  
  ## colorChange()
  ## prints out the current value of array  $this->colors 
  ## Steps to next index or 0 if end is reached
  function colorChange()  {
    $currentColor = $this->colors[$this->colorindex];
    $this->colorindex = ($this->colorindex == (count($this->colors) - 1)) ? 0 : $this->colorindex + 1;
    return $currentColor;
    }
    
  # Splits the template $str into parts saves it to $this->template.
  # Afterwards each element contains either
  # - Only text
  # - an HTML comment
  # - exactly one parser tag
  function splitTemplate($str) {
    $sicherheitscounter = 0;
    $this->template = array();
    while ($str != '') {
      $result = preg_match('/^(.*?)(<#.*?#>|<!--.*?-->|\n?$)/s', $str, $matches);
      $str = substr($str, strlen($matches[0]));
      if (strlen($matches[1])) $this->template[] = $matches[1];
      if (preg_match('/<#INCLUDE (.*)#>/', $matches[2], $matches2)) {
        $str = $this->loadInclude($matches2[1]).$str;
      } 
      elseif (preg_match('/<#ATTR (.*)#>/', $matches[2], $matches2))  {
        $this->colorSet($matches2[1]);
      }
      else {
        if (strlen($matches[2])) $this->template[] = $matches[2];
      }
      if (++$sicherheitscounter >= 2000) {
        print_r($matches);
        die("Parser stuck: '".$str."' $result ".strlen($str));
      }
    }
  }
  
  # loads an Include file and returns its contents
  function loadInclude($name) {
    $path = $this->includePath.$name;
    return file_get_contents($path);
  }

  # calls a variable or constant
  function getvar(&$vars, $var) {
    if ($var == "ATTR")
      return $this->colorChange();
    elseif (array_key_exists($var, $vars))
      return $vars[$var];
    elseif ($this->paramObject)
      return $this->paramObject->getVar($var);
    elseif (preg_match('/^G_/', $var) && defined($var))
      return constant($var);
    else
      return '';
  }
  
  function findEndOfIF($j, $to, $var, $tag) {
    $nest = 1;
    while ($j < $to) {
      if (preg_match("|^<#IF |", $this->template[$j])) {
        ++$nest;
        #echo "nest+ $nest: ".$this->template[$j]."<br>";
      } elseif (preg_match("|^<#/IF |", $this->template[$j])) {
        --$nest;
        #echo "nest- $nest: ".$this->template[$j]."<br>";
      }
      if ($nest <= 0) break;
      ++$j;
    }
    #while ($j < $to && !preg_match("|<#/IF !?$var#>|", $this->template[$j])) ++$j;
    
    if ($j >= $to) {
      echo "<br>WARNING: $tag not closed<br>"; 
    }
    
    return $j;
  }
  
  # works its way through the entries  $this->template[$from] until $this->template[$to-1]
  # using the parameters $vars and appends the result to $this->output
  # $enable: Output mode: 0=disabled; 1=active; -1=disabled, to be enabled with ELSE 
  function process($from, $to, $vars, $enable = 1) {
    for ($i = $from; $i < $to; $i++) {
      $p = $this->template[$i];
      if ($enable != 1) {
        # nur nach ELSE und geschachtelten IFs suchen
        if ($p == "<#ELSE#>") {
          $enable = -$enable;
        } elseif (preg_match('/^<#IF (!?)(.*)?#>/', $p, $matches)) {
          $var = $matches[2];
          if (preg_match('/(\S*)\s+\(?(.*?)\)?$/', $var, $matches))
            $var = $matches[1];
          
          ++$i;
          $j = $this->findEndOfIF($i, $to, $var, $p);
          
          # call process() recursively but don't output anything
          $this->process($i, $j, $vars, 0);
            
          # Proceed after closing tag
          $i = $j;
        }        
      } elseif (preg_match("/^<#FOR (.*)#>/", $p, $matches)) {
        # find ends of FOR tags
        $var = $matches[1];
        $value = $this->getvar($vars, $var);
        $j = ++$i;
        while ($j < $to && $this->template[$j] != "<#/FOR $var#>") ++$j;
        if ($j >= $to) die("Schließendes Tag für $p fehlt");
        
        # call process() recursively for each line
        if (is_array($value)) foreach ($value as $row) {
          if (!is_array($row)) $row = array('ROW' => $row);
          $this->process($i, $j, $row + $vars, 1);
        }
          
        # Proceed after closing tag
        $i = $j;
      } elseif (preg_match('/^<#IF (!?)(.*)?#>/', $p, $matches)) {
        # Split tag
        $neg = $matches[1];
        $var = $matches[2];
        $cond = '';
        if (preg_match('/(\S*)\s+\(?(.*?)\)?$/', $var, $matches)) {
          $var = $matches[1];
          $cond = $matches[2];
        }
        $value = $this->getvar($vars, $var);
        if ($neg) $value = !$value;
        if ($cond) $value = $this->checkCondition($value, $cond);
        
        ++$i;
        $j = $this->findEndOfIF($i, $to, $var, $p);
        
        # call process() recursively if variable is set
        $this->process($i, $j, $vars, $value ? 1 : -1);
          
        # Proceed after closing tag
        $i = $j;
      } elseif ($p == "<#ELSE#>") {
        $enable = -$enable;
      } elseif (preg_match("/^<#(.*)#>/", $p, $matches)) {
        # Variablen-Wert ausgeben
        $this->append($this->getvar($vars, $matches[1]));
      } else { # Normaler Text
        $this->append($p);
      }
    }
  }

  ## Prints PHP code to the output page
  function CheckPHP($text) {
  eval('?>'.$text.'<?'); 
  }
  
  function append($text) {
    if (is_array($this->output))
      $this->output[] = $text;
    else
      echo $text;
  }  

  ########################################################
  ################ PUBLIC FUNCTIONS ################
  ########################################################
  
  # constructor
  function Parser2() {
    global $ADM_SESS;
    if(isset($ADM_SESS['PERM_USERNAME'])) $this->params['ADMIN_USERNAME'] = $ADM_SESS['PERM_USERNAME'];
  }
  
  # Returns all set parameters
  function getParams() {
    return $this->params;
  }
  
  # Returns ONE set parameter
  function getParam($name) {
    return $this->params[$name];
  }

  # Sets one parameter
  function setParam($name, $value) {
    $this->params[$name] = $value;
  }
  
  # sets several parameters at once
  # accepts an array or an object that supports the method getVar($name)
  function setParams(&$params) {
    if (is_array($params))
      $this->params = $params + $this->params;
    elseif (is_object($params))
      $this->paramObject = $params;
  }
  
  # Deletes all parameters (no argument)
  # or a list of parameters from an array
  # (the parameters can be keys or values)
  function clearParams($array = 'all') {
    if ($array == 'all') {
      $this->params = array();
      $this->paramObject = null;
    } else {
      foreach ($array as $k => $v) {
        unset($this->params[$k]);
        unset($this->params[$v]);
      }
    }
  }
  
  # Deletes one parameter
  function clearParam($name) {
    unset($this->params[$name]);
  }
  
  # Assembles a template from a frame document and fragments
  function assemble($frame, $frags) {
    $tmpl = file_get_contents($frame);
    
    foreach ($frags AS $fragname => $fragpath) {
      $cmd = "|<!--INSERT_$fragname-->|";
      if (preg_match($cmd, $tmpl)) {
  	    $tmpl = preg_replace($cmd, file_get_contents($fragpath), $tmpl);
      }
    }
    
    $this->splitTemplate($tmpl);
  }
  
  # Load a monolithic template
  function setTemplate($tmpl) {
    if (!is_file($tmpl))
      die("Template not found: $tmpl");
    $idx = strrpos($tmpl, '/');
    if (!isset($this->includePath))
      $this->includePath = substr($tmpl, 0, $idx === false ? 0 : $idx+1);
    $this->splitTemplate(file_get_contents($tmpl));
  }
  
  # Sets the template content directly (not through a file)
  function setTemplateText($text) {
    $this->splitTemplate($text);
  }
  
  # Parse template and return the contents
  function parseAndReturn() {
    $this->output = array();
    $this->process(0, count($this->template), $this->params);
    return join('', $this->output);
  }
  
  # Parse template and ECHO the result
  function parseAndEcho() {
    $this->output = null;
    $this->process(0, count($this->template), $this->params);
  }
  
  # Parse template and ECHO the result;
  # Eval One-line PHP code
  function parseAndEchoPHP() {
    $this->CheckPHP($this->parseAndReturn());
  }
  
  # Parse template and save the result to the file $file
  function parseAndSave($file) {
    $outf = fopen($file, "w");
    fputs($outf, $this->parseAndReturn());
    fclose($outf);
  }
  
  # Set include path (only 1 directory possible)
  # Cal this before setTemplate!
  function setIncludePath($path) {
    $this->includePath = $path;
    if (substr($path, -1, 1) != '/') $this->includePath .= '/';
  }
  
  ########################################################
  ################# STATIC FUNCTIONS #################
  ########################################################
  
  # Does everything at once: assemble, setParams and parseAndEcho
  function assembleAndEcho($frame, $frags, $params) {
    $parser = new Parser2;
    $parser->assemble($frame, $frags);
    $parser->setParams($params);
    $parser->parseAndEcho();
  }
  
  
  # Does everything at once: setTemplate, setParams and parseAndEcho
  function setTemplateAndEcho($tmpl, $params) {
    $parser = new Parser2;
    $parser->setTemplate($tmpl);
    $parser->setParams($params);
    $parser->parseAndEcho();
  }
  
} # End class Parser2

# a parser that uses [ ] instead of <# #>
class AlternativeParser extends Parser2 {
  function splitTemplate($str) {
    $str = preg_replace('/\[(.*?)\]/e', "'<#'.strtolower('\\1').'#>'", $str);
    parent::splitTemplate($str);
  }
}
  
?>
Return current item: Simple Template Parser