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