Ignore:
Timestamp:
Mar 30, 2004, 12:40:21 AM (20 years ago)
Author:
z0rglub
Message:

code refactoring

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/template.php

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