]> scripts.mit.edu Git - autoinstalls/phpBB.git/blob - contrib/template_file_cache.php
phpBB 2.0.19
[autoinstalls/phpBB.git] / contrib / template_file_cache.php
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 ?>