home *** CD-ROM | disk | FTP | other *** search
/ PC Advisor 2006 January / PCA126_DVD.iso / ADVISORS / phpBB-2.0.17 / phpBB2 / includes / template.php < prev    next >
Encoding:
PHP Script  |  2005-07-19  |  14.0 KB  |  478 lines

  1. <?php
  2. /***************************************************************************
  3.  *                              template.php
  4.  *                            -------------------
  5.  *   begin                : Saturday, Feb 13, 2001
  6.  *   copyright            : (C) 2001 The phpBB Group
  7.  *   email                : support@phpbb.com
  8.  *
  9.  *   $Id: template.php,v 1.10.2.5 2005/05/06 20:50:11 acydburn Exp $
  10.  *
  11.  *
  12.  ***************************************************************************/
  13.  
  14. /***************************************************************************
  15.  *
  16.  *   This program is free software; you can redistribute it and/or modify
  17.  *   it under the terms of the GNU General Public License as published by
  18.  *   the Free Software Foundation; either version 2 of the License, or
  19.  *   (at your option) any later version.
  20.  *
  21.  ***************************************************************************/
  22.  
  23. /**
  24.  * Template class. By Nathan Codding of the phpBB group.
  25.  * The interface was originally inspired by PHPLib templates,
  26.  * and the template file formats are quite similar.
  27.  *
  28.  */
  29.  
  30. class Template {
  31.     var $classname = "Template";
  32.  
  33.     // variable that holds all the data we'll be substituting into
  34.     // the compiled templates.
  35.     // ...
  36.     // This will end up being a multi-dimensional array like this:
  37.     // $this->_tpldata[block.][iteration#][child.][iteration#][child2.][iteration#][variablename] == value
  38.     // if it's a root-level variable, it'll be like this:
  39.     // $this->_tpldata[.][0][varname] == value
  40.     var $_tpldata = array();
  41.  
  42.     // Hash of filenames for each template handle.
  43.     var $files = array();
  44.  
  45.     // Root template directory.
  46.     var $root = "";
  47.  
  48.     // this will hash handle names to the compiled code for that handle.
  49.     var $compiled_code = array();
  50.  
  51.     // This will hold the uncompiled code for that handle.
  52.     var $uncompiled_code = array();
  53.  
  54.     /**
  55.      * Constructor. Simply sets the root dir.
  56.      *
  57.      */
  58.     function Template($root = ".")
  59.     {
  60.         $this->set_rootdir($root);
  61.     }
  62.  
  63.     /**
  64.      * Destroys this template object. Should be called when you're done with it, in order
  65.      * to clear out the template data so you can load/parse a new template set.
  66.      */
  67.     function destroy()
  68.     {
  69.         $this->_tpldata = array();
  70.     }
  71.  
  72.     /**
  73.      * Sets the template root directory for this Template object.
  74.      */
  75.     function set_rootdir($dir)
  76.     {
  77.         if (!is_dir($dir))
  78.         {
  79.             return false;
  80.         }
  81.  
  82.         $this->root = $dir;
  83.         return true;
  84.     }
  85.  
  86.     /**
  87.      * Sets the template filenames for handles. $filename_array
  88.      * should be a hash of handle => filename pairs.
  89.      */
  90.     function set_filenames($filename_array)
  91.     {
  92.         if (!is_array($filename_array))
  93.         {
  94.             return false;
  95.         }
  96.  
  97.         reset($filename_array);
  98.         while(list($handle, $filename) = each($filename_array))
  99.         {
  100.             $this->files[$handle] = $this->make_filename($filename);
  101.         }
  102.  
  103.         return true;
  104.     }
  105.  
  106.  
  107.     /**
  108.      * Load the file for the handle, compile the file,
  109.      * and run the compiled code. This will print out
  110.      * the results of executing the template.
  111.      */
  112.     function pparse($handle)
  113.     {
  114.         if (!$this->loadfile($handle))
  115.         {
  116.             die("Template->pparse(): Couldn't load template file for handle $handle");
  117.         }
  118.  
  119.         // actually compile the template now.
  120.         if (!isset($this->compiled_code[$handle]) || empty($this->compiled_code[$handle]))
  121.         {
  122.             // Actually compile the code now.
  123.             $this->compiled_code[$handle] = $this->compile($this->uncompiled_code[$handle]);
  124.         }
  125.  
  126.         // Run the compiled code.
  127.         eval($this->compiled_code[$handle]);
  128.         return true;
  129.     }
  130.  
  131.     /**
  132.      * Inserts the uncompiled code for $handle as the
  133.      * value of $varname in the root-level. This can be used
  134.      * to effectively include a template in the middle of another
  135.      * template.
  136.      * Note that all desired assignments to the variables in $handle should be done
  137.      * BEFORE calling this function.
  138.      */
  139.     function assign_var_from_handle($varname, $handle)
  140.     {
  141.         if (!$this->loadfile($handle))
  142.         {
  143.             die("Template->assign_var_from_handle(): Couldn't load template file for handle $handle");
  144.         }
  145.  
  146.         // Compile it, with the "no echo statements" option on.
  147.         $_str = "";
  148.         $code = $this->compile($this->uncompiled_code[$handle], true, '_str');
  149.  
  150.         // evaluate the variable assignment.
  151.         eval($code);
  152.         // assign the value of the generated variable to the given varname.
  153.         $this->assign_var($varname, $_str);
  154.  
  155.         return true;
  156.     }
  157.  
  158.     /**
  159.      * Block-level variable assignment. Adds a new block iteration with the given
  160.      * variable assignments. Note that this should only be called once per block
  161.      * iteration.
  162.      */
  163.     function assign_block_vars($blockname, $vararray)
  164.     {
  165.         if (strstr($blockname, '.'))
  166.         {
  167.             // Nested block.
  168.             $blocks = explode('.', $blockname);
  169.             $blockcount = sizeof($blocks) - 1;
  170.             $str = '$this->_tpldata';
  171.             for ($i = 0; $i < $blockcount; $i++)
  172.             {
  173.                 $str .= '[\'' . $blocks[$i] . '.\']';
  174.                 eval('$lastiteration = sizeof(' . $str . ') - 1;');
  175.                 $str .= '[' . $lastiteration . ']';
  176.             }
  177.             // Now we add the block that we're actually assigning to.
  178.             // We're adding a new iteration to this block with the given
  179.             // variable assignments.
  180.             $str .= '[\'' . $blocks[$blockcount] . '.\'][] = $vararray;';
  181.  
  182.             // Now we evaluate this assignment we've built up.
  183.             eval($str);
  184.         }
  185.         else
  186.         {
  187.             // Top-level block.
  188.             // Add a new iteration to this block with the variable assignments
  189.             // we were given.
  190.             $this->_tpldata[$blockname . '.'][] = $vararray;
  191.         }
  192.  
  193.         return true;
  194.     }
  195.  
  196.     /**
  197.      * Root-level variable assignment. Adds to current assignments, overriding
  198.      * any existing variable assignment with the same name.
  199.      */
  200.     function assign_vars($vararray)
  201.     {
  202.         reset ($vararray);
  203.         while (list($key, $val) = each($vararray))
  204.         {
  205.             $this->_tpldata['.'][0][$key] = $val;
  206.         }
  207.  
  208.         return true;
  209.     }
  210.  
  211.     /**
  212.      * Root-level variable assignment. Adds to current assignments, overriding
  213.      * any existing variable assignment with the same name.
  214.      */
  215.     function assign_var($varname, $varval)
  216.     {
  217.         $this->_tpldata['.'][0][$varname] = $varval;
  218.  
  219.         return true;
  220.     }
  221.  
  222.  
  223.     /**
  224.      * Generates a full path+filename for the given filename, which can either
  225.      * be an absolute name, or a name relative to the rootdir for this Template
  226.      * object.
  227.      */
  228.     function make_filename($filename)
  229.     {
  230.         // Check if it's an absolute or relative path.
  231.         if (substr($filename, 0, 1) != '/')
  232.         {
  233.                $filename = ($rp_filename = phpbb_realpath($this->root . '/' . $filename)) ? $rp_filename : $filename;
  234.         }
  235.  
  236.         if (!file_exists($filename))
  237.         {
  238.             die("Template->make_filename(): Error - file $filename does not exist");
  239.         }
  240.  
  241.         return $filename;
  242.     }
  243.  
  244.  
  245.     /**
  246.      * If not already done, load the file for the given handle and populate
  247.      * the uncompiled_code[] hash with its code. Do not compile.
  248.      */
  249.     function loadfile($handle)
  250.     {
  251.         // If the file for this handle is already loaded and compiled, do nothing.
  252.         if (isset($this->uncompiled_code[$handle]) && !empty($this->uncompiled_code[$handle]))
  253.         {
  254.             return true;
  255.         }
  256.  
  257.         // If we don't have a file assigned to this handle, die.
  258.         if (!isset($this->files[$handle]))
  259.         {
  260.             die("Template->loadfile(): No file specified for handle $handle");
  261.         }
  262.  
  263.         $filename = $this->files[$handle];
  264.  
  265.         $str = implode("", @file($filename));
  266.         if (empty($str))
  267.         {
  268.             die("Template->loadfile(): File $filename for handle $handle is empty");
  269.         }
  270.  
  271.         $this->uncompiled_code[$handle] = $str;
  272.  
  273.         return true;
  274.     }
  275.  
  276.  
  277.  
  278.     /**
  279.      * Compiles the given string of code, and returns
  280.      * the result in a string.
  281.      * If "do_not_echo" is true, the returned code will not be directly
  282.      * executable, but can be used as part of a variable assignment
  283.      * for use in assign_code_from_handle().
  284.      */
  285.     function compile($code, $do_not_echo = false, $retvar = '')
  286.     {
  287.         // replace \ with \\ and then ' with \'.
  288.         $code = str_replace('\\', '\\\\', $code);
  289.         $code = str_replace('\'', '\\\'', $code);
  290.  
  291.         // change template varrefs into PHP varrefs
  292.  
  293.         // This one will handle varrefs WITH namespaces
  294.         $varrefs = array();
  295.         preg_match_all('#\{(([a-z0-9\-_]+?\.)+?)([a-z0-9\-_]+?)\}#is', $code, $varrefs);
  296.         $varcount = sizeof($varrefs[1]);
  297.         for ($i = 0; $i < $varcount; $i++)
  298.         {
  299.             $namespace = $varrefs[1][$i];
  300.             $varname = $varrefs[3][$i];
  301.             $new = $this->generate_block_varref($namespace, $varname);
  302.  
  303.             $code = str_replace($varrefs[0][$i], $new, $code);
  304.         }
  305.  
  306.         // This will handle the remaining root-level varrefs
  307.         $code = preg_replace('#\{([a-z0-9\-_]*?)\}#is', '\' . ( ( isset($this->_tpldata[\'.\'][0][\'\1\']) ) ? $this->_tpldata[\'.\'][0][\'\1\'] : \'\' ) . \'', $code);
  308.  
  309.         // Break it up into lines.
  310.         $code_lines = explode("\n", $code);
  311.  
  312.         $block_nesting_level = 0;
  313.         $block_names = array();
  314.         $block_names[0] = ".";
  315.  
  316.         // Second: prepend echo ', append ' . "\n"; to each line.
  317.         $line_count = sizeof($code_lines);
  318.         for ($i = 0; $i < $line_count; $i++)
  319.         {
  320.             $code_lines[$i] = chop($code_lines[$i]);
  321.             if (preg_match('#<!-- BEGIN (.*?) -->#', $code_lines[$i], $m))
  322.             {
  323.                 $n[0] = $m[0];
  324.                 $n[1] = $m[1];
  325.  
  326.                 // Added: dougk_ff7-Keeps templates from bombing if begin is on the same line as end.. I think. :)
  327.                 if ( preg_match('#<!-- END (.*?) -->#', $code_lines[$i], $n) )
  328.                 {
  329.                     $block_nesting_level++;
  330.                     $block_names[$block_nesting_level] = $m[1];
  331.                     if ($block_nesting_level < 2)
  332.                     {
  333.                         // Block is not nested.
  334.                         $code_lines[$i] = '$_' . $n[1] . '_count = ( isset($this->_tpldata[\'' . $n[1] . '.\']) ) ?  sizeof($this->_tpldata[\'' . $n[1] . '.\']) : 0;';
  335.                         $code_lines[$i] .= "\n" . 'for ($_' . $n[1] . '_i = 0; $_' . $n[1] . '_i < $_' . $n[1] . '_count; $_' . $n[1] . '_i++)';
  336.                         $code_lines[$i] .= "\n" . '{';
  337.                     }
  338.                     else
  339.                     {
  340.                         // This block is nested.
  341.  
  342.                         // Generate a namespace string for this block.
  343.                         $namespace = implode('.', $block_names);
  344.                         // strip leading period from root level..
  345.                         $namespace = substr($namespace, 2);
  346.                         // Get a reference to the data array for this block that depends on the
  347.                         // current indices of all parent blocks.
  348.                         $varref = $this->generate_block_data_ref($namespace, false);
  349.                         // Create the for loop code to iterate over this block.
  350.                         $code_lines[$i] = '$_' . $n[1] . '_count = ( isset(' . $varref . ') ) ? sizeof(' . $varref . ') : 0;';
  351.                         $code_lines[$i] .= "\n" . 'for ($_' . $n[1] . '_i = 0; $_' . $n[1] . '_i < $_' . $n[1] . '_count; $_' . $n[1] . '_i++)';
  352.                         $code_lines[$i] .= "\n" . '{';
  353.                     }
  354.  
  355.                     // We have the end of a block.
  356.                     unset($block_names[$block_nesting_level]);
  357.                     $block_nesting_level--;
  358.                     $code_lines[$i] .= '} // END ' . $n[1];
  359.                     $m[0] = $n[0];
  360.                     $m[1] = $n[1];
  361.                 }
  362.                 else
  363.                 {
  364.                     // We have the start of a block.
  365.                     $block_nesting_level++;
  366.                     $block_names[$block_nesting_level] = $m[1];
  367.                     if ($block_nesting_level < 2)
  368.                     {
  369.                         // Block is not nested.
  370.                         $code_lines[$i] = '$_' . $m[1] . '_count = ( isset($this->_tpldata[\'' . $m[1] . '.\']) ) ? sizeof($this->_tpldata[\'' . $m[1] . '.\']) : 0;';
  371.                         $code_lines[$i] .= "\n" . 'for ($_' . $m[1] . '_i = 0; $_' . $m[1] . '_i < $_' . $m[1] . '_count; $_' . $m[1] . '_i++)';
  372.                         $code_lines[$i] .= "\n" . '{';
  373.                     }
  374.                     else
  375.                     {
  376.                         // This block is nested.
  377.  
  378.                         // Generate a namespace string for this block.
  379.                         $namespace = implode('.', $block_names);
  380.                         // strip leading period from root level..
  381.                         $namespace = substr($namespace, 2);
  382.                         // Get a reference to the data array for this block that depends on the
  383.                         // current indices of all parent blocks.
  384.                         $varref = $this->generate_block_data_ref($namespace, false);
  385.                         // Create the for loop code to iterate over this block.
  386.                         $code_lines[$i] = '$_' . $m[1] . '_count = ( isset(' . $varref . ') ) ? sizeof(' . $varref . ') : 0;';
  387.                         $code_lines[$i] .= "\n" . 'for ($_' . $m[1] . '_i = 0; $_' . $m[1] . '_i < $_' . $m[1] . '_count; $_' . $m[1] . '_i++)';
  388.                         $code_lines[$i] .= "\n" . '{';
  389.                     }
  390.                 }
  391.             }
  392.             else if (preg_match('#<!-- END (.*?) -->#', $code_lines[$i], $m))
  393.             {
  394.                 // We have the end of a block.
  395.                 unset($block_names[$block_nesting_level]);
  396.                 $block_nesting_level--;
  397.                 $code_lines[$i] = '} // END ' . $m[1];
  398.             }
  399.             else
  400.             {
  401.                 // We have an ordinary line of code.
  402.                 if (!$do_not_echo)
  403.                 {
  404.                     $code_lines[$i] = 'echo \'' . $code_lines[$i] . '\' . "\\n";';
  405.                 }
  406.                 else
  407.                 {
  408.                     $code_lines[$i] = '$' . $retvar . '.= \'' . $code_lines[$i] . '\' . "\\n";'; 
  409.                 }
  410.             }
  411.         }
  412.  
  413.         // Bring it back into a single string of lines of code.
  414.         $code = implode("\n", $code_lines);
  415.         return $code    ;
  416.  
  417.     }
  418.  
  419.  
  420.     /**
  421.      * Generates a reference to the given variable inside the given (possibly nested)
  422.      * block namespace. This is a string of the form:
  423.      * ' . $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['varname'] . '
  424.      * It's ready to be inserted into an "echo" line in one of the templates.
  425.      * NOTE: expects a trailing "." on the namespace.
  426.      */
  427.     function generate_block_varref($namespace, $varname)
  428.     {
  429.         // Strip the trailing period.
  430.         $namespace = substr($namespace, 0, strlen($namespace) - 1);
  431.  
  432.         // Get a reference to the data block for this namespace.
  433.         $varref = $this->generate_block_data_ref($namespace, true);
  434.         // Prepend the necessary code to stick this in an echo line.
  435.  
  436.         // Append the variable reference.
  437.         $varref .= '[\'' . $varname . '\']';
  438.  
  439.         $varref = '\' . ( ( isset(' . $varref . ') ) ? ' . $varref . ' : \'\' ) . \'';
  440.  
  441.         return $varref;
  442.  
  443.     }
  444.  
  445.  
  446.     /**
  447.      * Generates a reference to the array of data values for the given
  448.      * (possibly nested) block namespace. This is a string of the form:
  449.      * $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN']
  450.      *
  451.      * If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above.
  452.      * NOTE: does not expect a trailing "." on the blockname.
  453.      */
  454.     function generate_block_data_ref($blockname, $include_last_iterator)
  455.     {
  456.         // Get an array of the blocks involved.
  457.         $blocks = explode(".", $blockname);
  458.         $blockcount = sizeof($blocks) - 1;
  459.         $varref = '$this->_tpldata';
  460.         // Build up the string with everything but the last child.
  461.         for ($i = 0; $i < $blockcount; $i++)
  462.         {
  463.             $varref .= '[\'' . $blocks[$i] . '.\'][$_' . $blocks[$i] . '_i]';
  464.         }
  465.         // Add the block reference for the last child.
  466.         $varref .= '[\'' . $blocks[$blockcount] . '.\']';
  467.         // Add the iterator for the last child if requried.
  468.         if ($include_last_iterator)
  469.         {
  470.             $varref .= '[$_' . $blocks[$blockcount] . '_i]';
  471.         }
  472.  
  473.         return $varref;
  474.     }
  475.  
  476. }
  477.  
  478. ?>