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_db_cache.php < prev    next >
Encoding:
PHP Script  |  2004-07-10  |  15.7 KB  |  536 lines

  1. <?php
  2. /***************************************************************************
  3.  *                              template.inc
  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.  *
  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.         global $db;
  61.  
  62.         $this->set_rootdir($root);
  63.         $this->db = &$db;
  64.  
  65.         $this->pparse_order = array();
  66.     }
  67.  
  68.     /**
  69.      * Destroys this template object. Should be called when you're done with it, in order
  70.      * to clear out the template data so you can load/parse a new template set.
  71.      */
  72.     function destroy()
  73.     {
  74.         $this->_tpldata = array();
  75.     }
  76.  
  77.     /**
  78.      * Sets the template root directory for this Template object.
  79.      */
  80.     function set_rootdir($dir)
  81.     {
  82.         if (!is_dir($dir))
  83.         {
  84.             return false;
  85.         }
  86.  
  87.         $this->root = $dir;
  88.         return true;
  89.     }
  90.  
  91.     /**
  92.      * Sets the template filenames for handles. $filename_array
  93.      * should be a hash of handle => filename pairs.
  94.      */
  95.     function set_filenames($filename_array)
  96.     {
  97.         global $table_prefix;
  98.  
  99.         if ( !is_array($filename_array) )
  100.         {
  101.             return false;
  102.         }
  103.  
  104.         $template_names = '';
  105.         @reset($filename_array);
  106.         while ( list($handle, $filename) = @each($filename_array) )
  107.         {
  108.             $this->files[$handle] = $this->make_filename($filename);
  109.             $template_names .= ( $template_names != '' ) ? ", '" . addslashes($this->files[$handle]) . "'" : "'" . addslashes($this->files[$handle]) . "'";
  110.         }
  111.  
  112.         $sql = "SELECT * 
  113.             FROM " . $table_prefix . "template_cache 
  114.             WHERE template_name IN ($template_names)";
  115.         if ( $result = $this->db->sql_query($sql) )
  116.         {
  117.             while( $row = $this->db->sql_fetchrow($result) )
  118.             {
  119.                 if( $row['template_cached'] == filemtime($row['template_name']) )
  120.                 {
  121.                     $this->compiled_code[$row['template_handle']] = $row['template_compile'];
  122.                     $this->echo_compiled[$row['template_handle']] = $row['template_echo'];
  123.                 }
  124.             } 
  125.         } 
  126.  
  127.         $this->db->sql_freeresult();
  128.  
  129.         return true;
  130.     }
  131.  
  132.  
  133.     /**
  134.      * Load the file for the handle, compile the file,
  135.      * and run the compiled code. This will print out
  136.      * the results of executing the template.
  137.      */
  138.     function pparse($handle)
  139.     {
  140.         global $table_prefix;
  141.  
  142.         if( empty($this->compiled_code[$handle]) )
  143.         {
  144.             if ( !$this->loadfile($handle) )
  145.             {
  146.                 die("Template->pparse(): Couldn't load template file for handle $handle");
  147.             }
  148.  
  149.             //
  150.             // Actually compile the code now.
  151.             //
  152.             $this->echo_compiled[$handle] = 1;
  153.             $this->compiled_code[$handle] = $this->compile($this->uncompiled_code[$handle]);
  154.  
  155.             $sql = "REPLACE INTO " . $table_prefix . "template_cache (template_name, template_handle, template_cached, template_compile) VALUES ('" . addslashes($this->files[$handle]) . "', '" . addslashes($handle) . "', " . filemtime($this->files[$handle]) . ", '" . addslashes($this->compiled_code[$handle]) . "')";
  156.             if ( !($result = $this->db->sql_query($sql)) )
  157.             {
  158.                 die("Couldn't insert template into cache!");
  159.             }
  160.  
  161.         }
  162.  
  163.         $_str = "";
  164.         eval($this->compiled_code[$handle]);
  165.  
  166.         if( $_str != "" ) 
  167.         {
  168.             echo $_str;
  169.         }
  170.  
  171.         return true;
  172.     }
  173.  
  174.     /**
  175.      * Inserts the uncompiled code for $handle as the
  176.      * value of $varname in the root-level. This can be used
  177.      * to effectively include a template in the middle of another
  178.      * template.
  179.      * Note that all desired assignments to the variables in $handle should be done
  180.      * BEFORE calling this function.
  181.      */
  182.     function assign_var_from_handle($varname, $handle)
  183.     {
  184.         global $table_prefix;
  185.  
  186.         if( empty($this->compiled_code[$handle]) )
  187.         {
  188.             if ( !$this->loadfile($handle) )
  189.             {
  190.                 die("Template->pparse(): Couldn't load template file for handle $handle");
  191.             }
  192.  
  193.             $code = $this->compile($this->uncompiled_code[$handle], true, '_str');
  194.  
  195.             $sql = "REPLACE INTO " . $table_prefix . "template_cache (template_name, template_handle, template_echo, template_cached, template_compile) VALUES ('" . addslashes($this->files[$handle]) . "', '" . addslashes($handle) . "', 0, " . filemtime($this->files[$handle]) . ", '" . addslashes($code) . "')";
  196.             if ( !($result = $this->db->sql_query($sql)) )
  197.             {
  198.                 die("Couldn't insert template into cache!");
  199.             }
  200.         }
  201.         else
  202.         {
  203.             $code = $this->compiled_code[$handle];
  204.         }
  205.  
  206.         // Compile It, With The "no Echo Statements" Option On.
  207.         $_str = "";
  208.         // evaluate the variable assignment.
  209.         eval($code);
  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.             $str = '$this->_tpldata';
  229.             for ($i = 0; $i < $blockcount; $i++)
  230.             {
  231.                 $str .= '[\'' . $blocks[$i] . '.\']';
  232.                 eval('$lastiteration = sizeof(' . $str . ') - 1;');
  233.                 $str .= '[' . $lastiteration . ']';
  234.             }
  235.             // Now we add the block that we're actually assigning to.
  236.             // We're adding a new iteration to this block with the given
  237.             // variable assignments.
  238.             $str .= '[\'' . $blocks[$blockcount] . '.\'][] = $vararray;';
  239.  
  240.             // Now we evaluate this assignment we've built up.
  241.             eval($str);
  242.         }
  243.         else
  244.         {
  245.             // Top-level block.
  246.             // Add a new iteration to this block with the variable assignments
  247.             // we were given.
  248.             $this->_tpldata[$blockname . '.'][] = $vararray;
  249.         }
  250.  
  251.         return true;
  252.     }
  253.  
  254.     /**
  255.      * Root-level variable assignment. Adds to current assignments, overriding
  256.      * any existing variable assignment with the same name.
  257.      */
  258.     function assign_vars($vararray)
  259.     {
  260.         reset ($vararray);
  261.         while (list($key, $val) = each($vararray))
  262.         {
  263.             $this->_tpldata['.'][0][$key] = $val;
  264.         }
  265.  
  266.         return true;
  267.     }
  268.  
  269.     /**
  270.      * Root-level variable assignment. Adds to current assignments, overriding
  271.      * any existing variable assignment with the same name.
  272.      */
  273.     function assign_var($varname, $varval)
  274.     {
  275.         $this->_tpldata['.'][0][$varname] = $varval;
  276.  
  277.         return true;
  278.     }
  279.  
  280.  
  281.     /**
  282.      * Generates a full path+filename for the given filename, which can either
  283.      * be an absolute name, or a name relative to the rootdir for this Template
  284.      * object.
  285.      */
  286.     function make_filename($filename)
  287.     {
  288.         // Check if it's an absolute or relative path.
  289.         if (substr($filename, 0, 1) != '/')
  290.         {
  291.             $filename = $this->root . '/' . $filename;
  292.         }
  293.  
  294.         if (!file_exists($filename))
  295.         {
  296.             die("Template->make_filename(): Error - file $filename does not exist");
  297.         }
  298.  
  299.         return $filename;
  300.     }
  301.  
  302.  
  303.     /**
  304.      * If not already done, load the file for the given handle and populate
  305.      * the uncompiled_code[] hash with its code. Do not compile.
  306.      */
  307.     function loadfile($handle)
  308.     {
  309.         // If the file for this handle is already loaded and compiled, do nothing.
  310.         if ( !empty($this->uncompiled_code[$handle]) )
  311.         {
  312.             return true;
  313.         }
  314.  
  315.         // If we don't have a file assigned to this handle, die.
  316.         if (!isset($this->files[$handle]))
  317.         {
  318.             die("Template->loadfile(): No file specified for handle $handle");
  319.         }
  320.  
  321.         $filename = $this->files[$handle];
  322.  
  323.         $str = implode("", @file($filename));
  324.         if (empty($str))
  325.         {
  326.             die("Template->loadfile(): File $filename for handle $handle is empty");
  327.         }
  328.  
  329.         $this->uncompiled_code[$handle] = $str;
  330.  
  331.         return true;
  332.     }
  333.  
  334.  
  335.  
  336.     /**
  337.      * Compiles the given string of code, and returns
  338.      * the result in a string.
  339.      * If "do_not_echo" is true, the returned code will not be directly
  340.      * executable, but can be used as part of a variable assignment
  341.      * for use in assign_code_from_handle().
  342.      */
  343.     function compile($code, $do_not_echo = false, $retvar = '')
  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);
  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', '\' . ( ( isset($this->_tpldata[\'.\'][0][\'\1\']) ) ? $this->_tpldata[\'.\'][0][\'\1\'] : \'\' ) . \'', $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] .= "\n" . 'for ($_' . $n[1] . '_i = 0; $_' . $n[1] . '_i < $_' . $n[1] . '_count; $_' . $n[1] . '_i++)';
  394.                         $code_lines[$i] .= "\n" . '{';
  395.                     }
  396.                     else
  397.                     {
  398.                         // This block is nested.
  399.  
  400.                         // Generate a namespace string for this block.
  401.                         $namespace = implode('.', $block_names);
  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] .= "\n" . 'for ($_' . $n[1] . '_i = 0; $_' . $n[1] . '_i < $_' . $n[1] . '_count; $_' . $n[1] . '_i++)';
  410.                         $code_lines[$i] .= "\n" . '{';
  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] .= "\n" . 'for ($_' . $m[1] . '_i = 0; $_' . $m[1] . '_i < $_' . $m[1] . '_count; $_' . $m[1] . '_i++)';
  430.                         $code_lines[$i] .= "\n" . '{';
  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] .= "\n" . 'for ($_' . $m[1] . '_i = 0; $_' . $m[1] . '_i < $_' . $m[1] . '_count; $_' . $m[1] . '_i++)';
  446.                         $code_lines[$i] .= "\n" . '{';
  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";';
  463.                 }
  464.                 else
  465.                 {
  466.                     $code_lines[$i] = '$' . $retvar . '.= \'' . $code_lines[$i] . '\' . "\\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.     /**
  479.      * Generates a reference to the given variable inside the given (possibly nested)
  480.      * block namespace. This is a string of the form:
  481.      * ' . $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['varname'] . '
  482.      * It's ready to be inserted into an "echo" line in one of the templates.
  483.      * NOTE: expects a trailing "." on the namespace.
  484.      */
  485.     function generate_block_varref($namespace, $varname)
  486.     {
  487.         // Strip the trailing period.
  488.         $namespace = substr($namespace, 0, strlen($namespace) - 1);
  489.  
  490.         // Get a reference to the data block for this namespace.
  491.         $varref = $this->generate_block_data_ref($namespace, true);
  492.         // Prepend the necessary code to stick this in an echo line.
  493.  
  494.         // Append the variable reference.
  495.         $varref .= '[\'' . $varname . '\']';
  496.  
  497.         $varref = '\' . ( ( isset(' . $varref . ') ) ? ' . $varref . ' : \'\' ) . \'';
  498.  
  499.         return $varref;
  500.  
  501.     }
  502.  
  503.  
  504.     /**
  505.      * Generates a reference to the array of data values for the given
  506.      * (possibly nested) block namespace. This is a string of the form:
  507.      * $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN']
  508.      *
  509.      * If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above.
  510.      * NOTE: does not expect a trailing "." on the blockname.
  511.      */
  512.     function generate_block_data_ref($blockname, $include_last_iterator)
  513.     {
  514.         // Get an array of the blocks involved.
  515.         $blocks = explode(".", $blockname);
  516.         $blockcount = sizeof($blocks) - 1;
  517.         $varref = '$this->_tpldata';
  518.         // Build up the string with everything but the last child.
  519.         for ($i = 0; $i < $blockcount; $i++)
  520.         {
  521.             $varref .= '[\'' . $blocks[$i] . '.\'][$_' . $blocks[$i] . '_i]';
  522.         }
  523.         // Add the block reference for the last child.
  524.         $varref .= '[\'' . $blocks[$blockcount] . '.\']';
  525.         // Add the iterator for the last child if requried.
  526.         if ($include_last_iterator)
  527.         {
  528.             $varref .= '[$_' . $blocks[$blockcount] . '_i]';
  529.         }
  530.  
  531.         return $varref;
  532.     }
  533.  
  534. }
  535.  
  536. ?>