/[pliva-si]/inc/Smarty.class.php
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /inc/Smarty.class.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (show annotations)
Tue Jul 3 12:37:17 2001 UTC (22 years, 10 months ago) by dpavlin
Branch: MAIN
Changes since 1.1: +1351 -1019 lines
update to Smarty 1.3.0

1 <?php
2 /*
3 * Project: Smarty: the PHP compiling template engine
4 * File: Smarty.class.php
5 * Author: Monte Ohrt <monte@ispi.net>
6 * Andrei Zmievski <andrei@ispi.net>
7 *
8 * Version: 1.3.0
9 * Copyright: 2001 ispi of Lincoln, Inc.
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 * You may contact the authors of Smarty by e-mail at:
26 * monte@ispi.net
27 * andrei@ispi.net
28 *
29 * Or, write to:
30 * Monte Ohrt
31 * CTO, ispi
32 * 237 S. 70th suite 220
33 * Lincoln, NE 68510
34 *
35 * The latest version of Smarty can be obtained from:
36 * http://www.phpinsider.com/
37 *
38 */
39
40 require('Smarty.addons.php');
41 require("Smarty.local.php");
42
43 define("SMARTY_PHP_PASSTHRU",0);
44 define("SMARTY_PHP_QUOTE",1);
45 define("SMARTY_PHP_REMOVE",2);
46 define("SMARTY_PHP_ALLOW",3);
47
48 class Smarty
49 {
50
51 // public vars
52 var $template_dir = './templates'; // name of directory for templates
53 var $compile_dir = './templates_c'; // name of directory for compiled templates
54 var $config_dir = './configs'; // directory where config files are located
55
56 var $global_assign = array('SCRIPT_NAME'); // variables from the GLOBALS array
57 // that are implicitly assigned
58 // to all templates
59 var $compile_check = true; // whether to check for compiling step or not:
60 // This is generally set to false once the
61 // application is entered into production and
62 // initially compiled. Leave set to true
63 // during development. true/false default true.
64
65 var $force_compile = false; // force templates to compile every time.
66 // if cache file exists, it will
67 // override compile_check and force_compile.
68 // true/false. default false.
69 var $caching = false; // whether to use caching or not. true/false
70 var $cache_dir = './cache'; // name of directory for template cache
71 var $cache_lifetime = 3600; // number of seconds cached content will persist.
72 // 0 = never expires. default is one hour (3600)
73
74 var $tpl_file_ext = '.tpl'; // template file extention
75
76 var $php_handling = SMARTY_PHP_PASSTHRU; // how smarty handles php tags
77 // possible values:
78 // SMARTY_PHP_PASSTHRU -> echo tags as is
79 // SMARTY_PHP_QUOTE -> escape tags as entities
80 // SMARTY_PHP_REMOVE -> remove php tags
81 // SMARTY_PHP_ALLOW -> execute php tags
82 // default: SMARTY_PHP_PASSTHRU
83
84 var $left_delimiter = '{'; // template tag delimiters.
85 var $right_delimiter = '}';
86
87
88 var $custom_funcs = array( 'html_options' => 'smarty_func_html_options',
89 'html_select_date' => 'smarty_func_html_select_date',
90 'header' => 'smarty_func_header',
91
92 //----- local custom_funcs
93 'img' => 'smarty_func_img',
94 'html_checkboxes' => 'smarty_func_html_checkboxes',
95 'input' => 'smarty_func_input',
96 'rinput' => 'smarty_func_rinput',
97
98 );
99
100 var $custom_mods = array( 'lower' => 'strtolower',
101 'upper' => 'strtoupper',
102 'capitalize' => 'ucwords',
103 'escape' => 'smarty_mod_escape',
104 'truncate' => 'smarty_mod_truncate',
105 'spacify' => 'smarty_mod_spacify',
106 'date_format' => 'smarty_mod_date_format',
107 'string_format' => 'smarty_mod_string_format',
108 'replace' => 'smarty_mod_replace',
109 'strip_tags' => 'smarty_mod_strip_tags',
110 'default' => 'smarty_mod_default',
111
112 //----- local custom_mods
113 'filesize' => 'smarty_mod_filesize',
114
115 );
116
117 // internal vars
118 var $_error_msg = false; // error messages. true/false
119 var $_tpl_vars = array();
120 var $_sectionelse_stack = array(); // keeps track of whether section had 'else' part
121 var $_literal_blocks = array(); // keeps literal template blocks
122 var $_current_file = null; // the current template being compiled
123 var $_current_line_no = 1; // line number for error messages
124 var $_smarty_md5 = 'f8d698aea36fcbead2b9d5359ffca76f'; // md5 checksum of the string 'Smarty'
125
126
127 /*======================================================================*\
128 Function: Smarty
129 Purpose: Constructor
130 \*======================================================================*/
131 function Smarty()
132 {
133 foreach ($this->global_assign as $var_name)
134 $this->assign($var_name, $GLOBALS[$var_name]);
135 }
136
137
138 /*======================================================================*\
139 Function: assign()
140 Purpose: assigns values to template variables
141 \*======================================================================*/
142
143 function assign($tpl_var, $value = NULL)
144 {
145 if (is_array($tpl_var)){
146 foreach ($tpl_var as $key => $val) {
147 if (!empty($key))
148 $this->_tpl_vars[$key] = $val;
149 }
150 } else {
151 if (!empty($tpl_var) && isset($value))
152 $this->_tpl_vars[$tpl_var] = $value;
153 }
154 }
155
156
157 /*======================================================================*\
158 Function: append
159 Purpose: appens values to template variables
160 \*======================================================================*/
161 function append($tpl_var, $value = NULL)
162 {
163 if (is_array($tpl_var)) {
164 foreach ($tpl_var as $key => $val) {
165 if (!empty($key)) {
166 if (!is_array($this->_tpl_vars[$key]))
167 settype($this->_tpl_vars[$key], 'array');
168 $this->_tpl_vars[$key][] = $val;
169 }
170 }
171 } else {
172 if (!empty($tpl_var) && isset($value)) {
173 if (!is_array($this->_tpl_vars[$tpl_var]))
174 settype($this->_tpl_vars[$tpl_var], 'array');
175 $this->_tpl_vars[$tpl_var][] = $value;
176 }
177 }
178 }
179
180
181 /*======================================================================*\
182 Function: clear_assign()
183 Purpose: clear the given assigned template variable.
184 \*======================================================================*/
185
186 function clear_assign($tpl_var)
187 {
188 if(is_array($tpl_var))
189 foreach($tpl_var as $curr_var)
190 unset($this->_tpl_vars[$curr_var]);
191 else
192 unset($this->_tpl_vars[$tpl_var]);
193 }
194
195
196 /*======================================================================*\
197 Function: register_function
198 Purpose: Registers custom function to be used in templates
199 \*======================================================================*/
200 function register_function($function, $function_impl)
201 {
202 $this->custom_funcs[$function] = $function_impl;
203 }
204
205
206 /*======================================================================*\
207 Function: register_modifier
208 Purpose: Registers modifier to be used in templates
209 \*======================================================================*/
210 function register_modifier($modifier, $modifier_impl)
211 {
212 $this->custom_mods[$modifier] = $modifier_impl;
213 }
214
215
216 /*======================================================================*\
217 Function: clear_cache()
218 Purpose: clear cached content for the given template and cache id
219 \*======================================================================*/
220
221 function clear_cache($tpl_file, $cache_id = null)
222 {
223 $cache_tpl_md5 = md5(realpath($this->template_dir.'/'.$tpl_file));
224 $cache_dir = $this->cache_dir.'/'.$cache_tpl_md5;
225
226 if (!is_dir($cache_dir))
227 return false;
228
229 if (isset($cache_id)) {
230 $cache_id_md5 = md5($cache_id);
231 $cache_id_dir = substr($cache_id_md5, 0, 2);
232 $cache_file = "$cache_dir/$cache_id_dir/{$cache_tpl_md5}_$cache_id_md5.cache";
233 return (bool)(is_file($cache_file) && unlink($cache_file));
234 } else
235 return $this->_clear_tpl_cache_dir($cache_tpl_md5);
236 }
237
238
239 /*======================================================================*\
240 Function: clear_all_cache()
241 Purpose: clear the entire contents of cache (all templates)
242 \*======================================================================*/
243
244 function clear_all_cache()
245 {
246 if (!is_dir($this->cache_dir))
247 return false;
248
249 $dir_handle = opendir($this->cache_dir);
250 while ($curr_dir = readdir($dir_handle)) {
251 if ($curr_dir == '.' || $curr_dir == '..' ||
252 !is_dir($this->cache_dir.'/'.$curr_dir))
253 continue;
254
255 $this->_clear_tpl_cache_dir($curr_dir);
256 }
257 closedir($dir_handle);
258
259 return true;
260 }
261
262
263 /*======================================================================*\
264 Function: is_cached()
265 Purpose: test to see if valid cache exists for this template
266 \*======================================================================*/
267
268 function is_cached($tpl_file, $cache_id = null)
269 {
270 if (!$this->caching)
271 return false;
272
273 // cache name = template path + cache_id
274 $cache_tpl_md5 = md5(realpath($this->template_dir.'/'.$tpl_file));
275 $cache_id_md5 = md5($cache_id);
276 $cache_id_dir = substr($cache_id_md5, 0, 2);
277 $cache_file = $this->cache_dir."/$cache_tpl_md5/$cache_id_dir/{$cache_tpl_md5}_$cache_id_md5.cache";
278
279 if (file_exists($cache_file) &&
280 ($this->cache_lifetime == 0 ||
281 (time() - filemtime($cache_file) <= $this->cache_lifetime)))
282 return true;
283 else
284 return false;
285
286 }
287
288
289 /*======================================================================*\
290 Function: clear_all_assign()
291 Purpose: clear all the assigned template variables.
292 \*======================================================================*/
293
294 function clear_all_assign()
295 {
296 $this->_tpl_vars = array();
297 }
298
299
300 /*======================================================================*\
301 Function: get_template_vars
302 Purpose: Returns an array containing template variables
303 \*======================================================================*/
304 function &get_template_vars()
305 {
306 return $this->_tpl_vars;
307 }
308
309
310 /*======================================================================*\
311 Function: display()
312 Purpose: executes & displays the template results
313 \*======================================================================*/
314
315 function display($tpl_file, $cache_id = null)
316 {
317 $this->fetch($tpl_file, $cache_id, true);
318 }
319
320 /*======================================================================*\
321 Function: fetch()
322 Purpose: executes & returns or displays the template results
323 \*======================================================================*/
324
325 function fetch($tpl_file, $cache_id = null, $display = false)
326 {
327 global $HTTP_SERVER_VARS;
328
329 if ($this->caching) {
330 // cache name = template path + cache_id
331 $cache_tpl_md5 = md5(realpath($this->template_dir.'/'.$tpl_file));
332 $cache_id_md5 = md5($cache_id);
333 $cache_id_dir = substr($cache_id_md5, 0, 2);
334 $cache_file = $this->cache_dir."/$cache_tpl_md5/$cache_id_dir/{$cache_tpl_md5}_$cache_id_md5.cache";
335
336 if (file_exists($cache_file) &&
337 ($this->cache_lifetime == 0 ||
338 (time() - filemtime($cache_file) <= $this->cache_lifetime))) {
339 $results = $this->_read_file($cache_file);
340 $results = $this->_process_cached_inserts($results);
341 if ($display) {
342 echo $results;
343 return;
344 } else
345 return $results;
346 }
347 }
348
349 // compile files
350 $this->_compile($this->template_dir);
351 //assemble compile directory path to file
352 $_compile_file = $this->compile_dir."/".$tpl_file.".php";
353
354 extract($this->_tpl_vars);
355
356 // if we just need to display the results, don't perform output
357 // buffering - for speed
358 if ($display && !$this->caching)
359 include($_compile_file);
360 else {
361 ob_start();
362 include($_compile_file);
363 $results = ob_get_contents();
364 ob_end_clean();
365 }
366
367 if($this->caching) {
368 $this->_write_file($cache_file, $results, true);
369 $results = $this->_process_cached_inserts($results);
370 }
371
372 if ($display) {
373 echo $results;
374 return;
375 } else
376 return $results;
377 }
378
379 /*======================================================================*\
380 Function: compile()
381 Purpose: called to compile the templates
382 \*======================================================================*/
383
384 function _compile($tpl_dir)
385 {
386 if($this->compile_check || $this->force_compile)
387 {
388 if($this->_traverse_files($tpl_dir, 0))
389 return true;
390 else
391 return false;
392 } else
393 return false;
394 }
395
396 /*======================================================================*\
397 Function: _traverse_files()
398 Purpose: traverse the template files & process each one
399 \*======================================================================*/
400
401 function _traverse_files($tpl_dir, $depth)
402 {
403 $retval = true;
404
405 if (is_dir($tpl_dir)) {
406 $dir_handle = opendir($tpl_dir);
407 while ($curr_file = readdir($dir_handle)) {
408 if ($curr_file == '.' || $curr_file == '..')
409 continue;
410
411 $filepath = $tpl_dir.'/'.$curr_file;
412 if (is_readable($filepath)) {
413 if (is_file($filepath) && substr($curr_file, -strlen($this->tpl_file_ext)) == $this->tpl_file_ext) {
414 if (!$this->_process_file($filepath)) {
415 $retval = false;
416 break;
417 }
418 } else if (is_dir($filepath)) {
419 if (!$this->_traverse_files($filepath, $depth + 1)) {
420 $retval = false;
421 break;
422 }
423 } else {
424 // invalid file type, skipping
425 $this->_set_error_msg("Invalid filetype for $filepath, skipping");
426 continue;
427 }
428 }
429 }
430
431 closedir($dir_handle);
432 return $retval;
433 } else {
434 $this->_set_error_msg("Directory \"$tpl_dir\" does not exist or is not a directory.");
435 return false;
436 }
437 }
438
439 /*======================================================================*\
440 Function: _process_file()
441 Input: test template files for modifications
442 and execute the compilation for each
443 one requiring it.
444 \*======================================================================*/
445
446 function _process_file($filepath)
447 {
448 if(preg_match("/^(.+)\/([^\/]+)$/", $filepath, $match)) {
449 $tpl_file_dir = $match[1];
450 $tpl_file_name = $match[2] . '.php';
451
452 $compile_dir = preg_replace('!^' . preg_quote($this->template_dir, '!') . '!',
453 $this->compile_dir, $match[1]);
454
455 //create directory if none exists
456 $this->_create_dir_structure($compile_dir);
457
458 // compile the template file if none exists or has been modified or recompile is forced
459 if ($this->force_compile || !file_exists($compile_dir."/".$tpl_file_name) ||
460 ($this->_modified_file($filepath, $compile_dir."/".$tpl_file_name))) {
461 if (!$this->_compile_file($filepath, $compile_dir."/".$tpl_file_name))
462 return false;
463 } else {
464 // no compilation needed
465 return true;
466 }
467 } else {
468 $this->_set_error_msg("problem matching \"$filepath.\"");
469 return false;
470 }
471
472 return true;
473 }
474
475 /*======================================================================*\
476 Function: _create_dir_structure
477 Purpose: create full directory structure
478 \*======================================================================*/
479 function _create_dir_structure($dir)
480 {
481 if (!file_exists($dir)) {
482 $dir_parts = preg_split('!/+!', $dir, -1, PREG_SPLIT_NO_EMPTY);
483 $new_dir = ($dir{0} == '/') ? '/' : '';
484 foreach ($dir_parts as $dir_part) {
485 $new_dir .= $dir_part;
486 if (!file_exists($new_dir) && !mkdir($new_dir, 0755)) {
487 $this->_set_error_msg("problem creating directory \"$dir\"");
488 return false;
489 }
490 $new_dir .= '/';
491 }
492 }
493 }
494
495 /*======================================================================*\
496 Function: _modified_file()
497 Input: return comparison of modification times of files
498 \*======================================================================*/
499
500 function _modified_file($filepath, $compilepath)
501 {
502 if (filemtime($filepath) >= filemtime($compilepath))
503 return true;
504 return false;
505 }
506
507 /*======================================================================*\
508 Function: _compile_file()
509 Input: compile a template file
510 \*======================================================================*/
511
512 function _compile_file($filepath, $compilepath)
513 {
514 if (!($template_contents = $this->_read_file($filepath)))
515 return false;
516
517 $this->_current_file = str_replace($this->template_dir . '/', '', $filepath);
518 $this->_current_line_no = 1;
519 $ldq = preg_quote($this->left_delimiter, '!');
520 $rdq = preg_quote($this->right_delimiter, '!');
521
522 /* Pull out the literal blocks. */
523 preg_match_all("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", $template_contents, $match);
524 $this->_literal_blocks = $match[1];
525 $template_contents = preg_replace("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s",
526 '{literal}', $template_contents);
527
528 /* Gather all template tags. */
529 preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $template_contents, $match);
530 $template_tags = $match[1];
531 /* Split content by template tags to obtain non-template content. */
532 $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $template_contents);
533
534 /* TODO: speed up the following with preg_replace and /F once we require that version of PHP */
535
536 /* loop through text blocks */
537 for ($curr_tb = 0; $curr_tb <= count($text_blocks); $curr_tb++) {
538 /* match anything within <? ?> */
539 if (preg_match_all('!(<\?[^?]*?\?>|<script\s+language\s*=\s*[\"\']?php[\"\']?\s*>)!is', $text_blocks[$curr_tb], $sp_match)) {
540 /* found at least one match, loop through each one */
541 for ($curr_sp = 0; $curr_sp < count($sp_match[0]); $curr_sp++) {
542 if (preg_match('!^(<\?(php\s|\s|=\s)|<script\s*language\s*=\s*[\"\']?php[\"\']?\s*>)!is', $sp_match[0][$curr_sp])) {
543 /* php tag */
544 if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
545 /* echo php contents */
546 $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '<?php echo \''.str_replace("'", "\'", $sp_match[0][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
547 } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
548 /* quote php tags */
549 $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], htmlspecialchars($sp_match[0][$curr_sp]), $text_blocks[$curr_tb]);
550 } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
551 /* remove php tags */
552 if (substr($sp_match[0][$curr_sp], 0, 2) == '<?')
553 $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '', $text_blocks[$curr_tb]);
554 else
555 /* attempt to remove everything between <script ...> and </script> */
556 $text_blocks[$curr_tb] = preg_replace('!'.preg_quote($sp_match[0][$curr_sp], '!').'.*?</script\s*>!is', '', $text_blocks[$curr_tb]);
557 }
558 } else
559 /* echo the non-php tags */
560 $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '<?php echo \''.str_replace("'", "\'", $sp_match[0][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
561 }
562 }
563 }
564
565 /* Compile the template tags into PHP code. */
566 $compiled_tags = array();
567 for ($i = 0; $i < count($template_tags); $i++) {
568 $this->_current_line_no += substr_count($text_blocks[$i], "\n");
569 $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
570 $this->_current_line_no += substr_count($template_tags[$i], "\n");
571 }
572
573 $compiled_contents = '';
574
575 /* Interleave the compiled contents and text blocks to get the final result. */
576 for ($i = 0; $i < count($compiled_tags); $i++) {
577 $compiled_contents .= $text_blocks[$i].$compiled_tags[$i];
578 }
579 $compiled_contents .= $text_blocks[$i];
580
581 /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
582 if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_contents, $match)) {
583 $strip_tags = $match[0];
584 $strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags);
585 $strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified);
586 for ($i = 0; $i < count($strip_tags); $i++)
587 $compiled_contents = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s",
588 $strip_tags_modified[$i], $compiled_contents, 1);
589 }
590
591 if(!$this->_write_file($compilepath, $compiled_contents))
592 return false;
593
594 return true;
595 }
596
597 /*======================================================================*\
598 Function: _process_cached_inserts
599 Purpose: Replace cached inserts with the actual results
600 \*======================================================================*/
601 function _process_cached_inserts($results)
602 {
603 preg_match_all('!'.$this->_smarty_md5.'{insert_cache (.*)}'.$this->_smarty_md5.'!Uis',
604 $results, $match);
605 list($cached_inserts, $insert_args) = $match;
606
607 for ($i = 0; $i < count($cached_inserts); $i++) {
608 $args = unserialize($insert_args[$i]);
609 $name = $args['name'];
610 unset($args['name']);
611
612 $function_name = 'insert_' . $name;
613 $replace = $function_name($args);
614
615 $results = str_replace($cached_inserts[$i], $replace, $results);
616 }
617
618 return $results;
619 }
620
621
622 /*======================================================================*\
623 Function: _compile_tag
624 Purpose: Compile a template tag
625 \*======================================================================*/
626 function _compile_tag($template_tag)
627 {
628 /* Matched comment. */
629 if ($template_tag{0} == '*' && $template_tag{strlen($template_tag)-1} == '*')
630 return '';
631
632 $qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
633
634 /* Split tag into two parts: command and the arguments. */
635 preg_match('/^(
636 (?: ' . $qstr_regexp . ' | (?>[^"\'\s]+))+
637 )
638 (?:\s+(.*))?
639 /xs', $template_tag, $match);
640 list(, $tag_command, $tag_args) = $match;
641
642 /* If the tag name matches a variable or section property definition,
643 we simply process it. */
644 if (preg_match('!^\$(\w+/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command) || // if a variable
645 preg_match('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command) || // or a configuration variable
646 preg_match('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command)) { // or a section property
647 settype($tag_command, 'array');
648 $this->_parse_vars_props($tag_command);
649 return "<?php echo $tag_command[0]; ?>\n";
650 }
651
652 switch ($tag_command) {
653 case 'include':
654 return $this->_compile_include_tag($tag_args);
655
656 case 'if':
657 return $this->_compile_if_tag($tag_args);
658
659 case 'else':
660 return '<?php else: ?>';
661
662 case 'elseif':
663 return $this->_compile_if_tag($tag_args, true);
664
665 case '/if':
666 return '<?php endif; ?>';
667
668 case 'ldelim':
669 return $this->left_delimiter;
670
671 case 'rdelim':
672 return $this->right_delimiter;
673
674 case 'section':
675 array_push($this->_sectionelse_stack, false);
676 return $this->_compile_section_start($tag_args);
677
678 case 'sectionelse':
679 $this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;
680 return "<?php endfor; else: ?>";
681
682 case '/section':
683 if (array_pop($this->_sectionelse_stack))
684 return "<?php endif; ?>";
685 else
686 return "<?php endfor; endif; ?>";
687
688 case 'config_load':
689 return $this->_compile_config_load_tag($tag_args);
690
691 case 'strip':
692 case '/strip':
693 return $this->left_delimiter.$tag_command.$this->right_delimiter;
694
695 case 'literal':
696 list (,$literal_block) = each($this->_literal_blocks);
697 $this->_current_line_no += substr_count($literal_block, "\n");
698 return $literal_block;
699
700 case 'insert':
701 return $this->_compile_insert_tag($tag_args);
702
703 default:
704 if (isset($this->custom_funcs[$tag_command])) {
705 return $this->_compile_custom_tag($tag_command, $tag_args);
706 } else {
707 $this->_syntax_error("unknown tag - '$tag_command'", E_USER_WARNING);
708 return;
709 }
710 }
711 }
712
713 function _compile_custom_tag($tag_command, $tag_args)
714 {
715 $function = $this->custom_funcs[$tag_command];
716
717 if (!function_exists($function)) {
718 $this->_syntax_error("custom function '$tag_command' is not implemented", E_USER_WARNING);
719 return;
720 }
721
722 $attrs = $this->_parse_attrs($tag_args);
723 foreach ($attrs as $arg_name => $arg_value) {
724 if (is_bool($arg_value))
725 $arg_value = $arg_value ? 'true' : 'false';
726 $arg_list[] = "'$arg_name' => $arg_value";
727 }
728
729 return "<?php $function(array(".implode(',', (array)$arg_list).")); ?>";
730 }
731
732
733 /*======================================================================*\
734 Function: _compile_insert_tag
735 Purpose: Compile {insert ...} tag
736 \*======================================================================*/
737 function _compile_insert_tag($tag_args)
738 {
739 $attrs = $this->_parse_attrs($tag_args);
740 $name = substr($attrs['name'], 1, -1);
741
742 if (empty($name)) {
743 $this->_syntax_error("missing insert name");
744 }
745
746 foreach ($attrs as $arg_name => $arg_value) {
747 if (is_bool($arg_value))
748 $arg_value = $arg_value ? 'true' : 'false';
749 $arg_list[] = "'$arg_name' => $arg_value";
750 }
751
752 return "<?php echo _smarty_insert_handler(array(".implode(', ', (array)$arg_list)."), \$this->caching, \$this->_smarty_md5); ?>\n";
753 }
754
755
756 /*======================================================================*\
757 Function: _compile_config_load_tag
758 Purpose: Compile {config_load ...} tag
759 \*======================================================================*/
760 function _compile_config_load_tag($tag_args)
761 {
762 $attrs = $this->_parse_attrs($tag_args);
763
764 if (empty($attrs['file'])) {
765 $this->_syntax_error("missing 'file' attribute in config_load tag");
766 }
767
768 $output = "<?php if (!class_exists('Config_File'))\n" .
769 " include_once 'Config_File.class.php';\n" .
770 "if (!is_object(\$_conf_obj) || get_class(\$_conf_obj) != 'config_file') {\n" .
771 " \$_conf_obj = new Config_File('".$this->config_dir."');\n" .
772 "}\n" .
773 "\$_config = array_merge((array)\$_config, \$_conf_obj->get(".$attrs['file']."));\n";
774
775 if (!empty($attrs['section']))
776 $output .= '$_config = array_merge((array)$_config, $_conf_obj->get('.$attrs['file'].', '.$attrs['section'].')); ';
777
778 $output .= '?>';
779
780 return $output;
781 }
782
783
784 /*======================================================================*\
785 Function: _compile_include_tag
786 Purpose: Compile {include ...} tag
787 \*======================================================================*/
788 function _compile_include_tag($tag_args)
789 {
790 $attrs = $this->_parse_attrs($tag_args);
791
792 if (empty($attrs['file'])) {
793 $this->_syntax_error("missing 'file' attribute in include tag");
794 } else
795 $attrs['file'] = $this->_dequote($attrs['file']);
796
797 if (count($attrs) > 1) {
798 $include_func_name = uniqid("_include_");
799 $include_file_name = $this->compile_dir.'/'.$attrs['file'];
800
801 foreach ($attrs as $arg_name => $arg_value) {
802 if ($arg_name == 'file') continue;
803 if (is_bool($arg_value))
804 $arg_value = $arg_value ? 'true' : 'false';
805 $arg_list[] = "'$arg_name' => $arg_value";
806 }
807
808 return "<?php " .
809 "if (!function_exists('$include_func_name')) {\n".
810 " function $include_func_name(\$file_name, \$def_vars, \$include_vars)\n" .
811 " {\n" .
812 " extract(\$def_vars);\n" .
813 " extract(\$include_vars);\n" .
814 " include \"\$file_name.php\";\n" .
815 " }\n" .
816 "}\n" .
817 "$include_func_name(\"$include_file_name\", get_defined_vars(), array(".implode(',', (array)$arg_list)."));\n?>";
818 } else
819 return '<?php include "'.$this->compile_dir.'/'.$attrs['file'].'.php"; ?>';
820 }
821
822
823 /*======================================================================*\
824 Function: _compile_section_start
825 Purpose: Compile {section ...} tag
826 \*======================================================================*/
827 function _compile_section_start($tag_args)
828 {
829 $attrs = $this->_parse_attrs($tag_args);
830
831 $output = "<?php ";
832 $section_name = $attrs['name'];
833 if (empty($section_name)) {
834 $this->_syntax_error("missing section name");
835 }
836
837 $output .= "unset(\$_sections[$section_name]);\n";
838 $section_props = "\$_sections[$section_name]['properties']";
839
840 foreach ($attrs as $attr_name => $attr_value) {
841 switch ($attr_name) {
842 case 'loop':
843 $output .= "{$section_props}['loop'] = is_array($attr_value) ? count($attr_value) : $attr_value;\n";
844 break;
845
846 case 'show':
847 if (is_bool($attr_value))
848 $attr_value = $attr_value ? 'true' : 'false';
849 $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
850 break;
851
852 default:
853 $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
854 break;
855 }
856 }
857
858 if (isset($attrs['loop'])) {
859 $loop_check_code = "{$section_props}['loop'] > 0 && ";
860 } else {
861 $output .= "{$section_props}['loop'] = 1;\n";
862 }
863
864 if (isset($attrs['show'])) {
865 $show_check_code = "{$section_props}['show'] && ";
866 } else {
867 $output .= "{$section_props}['show'] = {$section_props}['loop'] > 0;\n";
868 }
869
870 $output .= "if ($loop_check_code $show_check_code true): ";
871
872 $output .= "
873 for ({$section_props}['index'] = 0;
874 {$section_props}['index'] < {$section_props}['loop'];
875 {$section_props}['index']++):\n";
876 $output .= "{$section_props}['rownum'] = {$section_props}['index'] + 1;\n";
877
878 $output .= "?>";
879
880 return $output;
881 }
882
883
884 /*======================================================================*\
885 Function: _compile_if_tag
886 Purpose: Compile {if ...} tag
887 \*======================================================================*/
888 function _compile_if_tag($tag_args, $elseif = false)
889 {
890 /* Tokenize args for 'if' tag. */
891 preg_match_all('/(?:
892 "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | # match all double quoted strings allowed escaped double quotes
893 \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | # match all single quoted strings allowed escaped single quotes
894 [()] | # match parentheses
895 [^"\'\s()]+ # match any other token that is not any of the above
896 )/x', $tag_args, $match);
897 $tokens = $match[0];
898
899 $this->_parse_vars_props($tokens);
900
901 $is_arg_stack = array();
902
903 for ($i = 0; $i < count($tokens); $i++) {
904 $token = &$tokens[$i];
905 switch ($token) {
906 case 'eq':
907 $token = '==';
908 break;
909
910 case 'ne':
911 case 'neq':
912 $token = '!=';
913 break;
914
915 case 'lt':
916 $token = '<';
917 break;
918
919 case 'le':
920 case 'lte':
921 $token = '<=';
922 break;
923
924 case 'gt':
925 $token = '>';
926 break;
927
928 case 'ge':
929 case 'gte':
930 $token = '>=';
931 break;
932
933 case 'and':
934 $token = '&&';
935 break;
936
937 case 'or':
938 $token = '||';
939 break;
940
941 case 'not':
942 $token = '!';
943 break;
944
945 case 'mod':
946 $token = '%';
947 break;
948
949 case '(':
950 array_push($is_arg_stack, $i);
951 break;
952
953 case 'is':
954 /* If last token was a ')', we operate on the parenthesized
955 expression. The start of the expression is on the stack.
956 Otherwise, we operate on the last encountered token. */
957 if ($tokens[$i-1] == ')')
958 $is_arg_start = array_pop($is_arg_stack);
959 else
960 $is_arg_start = $i-1;
961 /* Construct the argument for 'is' expression, so it knows
962 what to operate on. */
963 $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
964
965 /* Pass all tokens from next one until the end to the
966 'is' expression parsing function. The function will
967 return modified tokens, where the first one is the result
968 of the 'is' expression and the rest are the tokens it
969 didn't touch. */
970 $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
971
972 /* Replace the old tokens with the new ones. */
973 array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
974
975 /* Adjust argument start so that it won't change from the
976 current position for the next iteration. */
977 $i = $is_arg_start;
978 break;
979 }
980 }
981
982 if ($elseif)
983 return '<?php elseif ('.implode(' ', $tokens).'): ?>';
984 else
985 return '<?php if ('.implode(' ', $tokens).'): ?>';
986 }
987
988 function _parse_is_expr($is_arg, $tokens)
989 {
990 $expr_end = 0;
991
992 if (($first_token = array_shift($tokens)) == 'not') {
993 $negate_expr = true;
994 $expr_type = array_shift($tokens);
995 } else
996 $expr_type = $first_token;
997
998 switch ($expr_type) {
999 case 'even':
1000 if ($tokens[$expr_end] == 'by') {
1001 $expr_end++;
1002 $expr_arg = $tokens[$expr_end++];
1003 $expr = "!(($is_arg / $expr_arg) % $expr_arg)";
1004 }
1005 else
1006 $expr = "!($is_arg % 2)";
1007 break;
1008
1009 case 'odd':
1010 if ($tokens[$expr_end] == 'by') {
1011 $expr_end++;
1012 $expr_arg = $tokens[$expr_end++];
1013 $expr = "(($is_arg / $expr_arg) % $expr_arg)";
1014 }
1015 else
1016 $expr = "($is_arg % 2)";
1017 break;
1018
1019 case 'div':
1020 if ($tokens[$expr_end] == 'by') {
1021 $expr_end++;
1022 $expr_arg = $tokens[$expr_end++];
1023 $expr = "!($is_arg % $expr_arg)";
1024 } else {
1025 $this->_syntax_error("expecting 'by' after 'div'");
1026 }
1027 break;
1028
1029 default:
1030 $this->_syntax_error("unknown 'is' expression - '$expr_type'");
1031 break;
1032 }
1033
1034 if ($negate_expr) {
1035 $expr = "!($expr)";
1036 }
1037
1038 array_splice($tokens, 0, $expr_end, $expr);
1039
1040 return $tokens;
1041 }
1042
1043
1044 /*======================================================================*\
1045 Function: _parse_attrs
1046 Purpose: Parse attribute string
1047 \*======================================================================*/
1048 function _parse_attrs($tag_args, $quote = true)
1049 {
1050 /* Tokenize tag attributes. */
1051 preg_match_all('/(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" |
1052 \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | (?>[^"\'=\s]+)
1053 )+ |
1054 [=]
1055 /x', $tag_args, $match);
1056 $tokens = $match[0];
1057 $var_delims = array('$', '#', '%');
1058
1059 $attrs = array();
1060 /* Parse state:
1061 0 - expecting attribute name
1062 1 - expecting '='
1063 2 - expecting attribute value (not '=') */
1064 $state = 0;
1065
1066 foreach ($tokens as $token) {
1067 switch ($state) {
1068 case 0:
1069 /* If the token is a valid identifier, we set attribute name
1070 and go to state 1. */
1071 if (preg_match('!^\w+$!', $token)) {
1072 $attr_name = $token;
1073 $state = 1;
1074 } else
1075 $this->_syntax_error("invalid attribute name - '$token'");
1076 break;
1077
1078 case 1:
1079 /* If the token is '=', then we go to state 2. */
1080 if ($token == '=') {
1081 $state = 2;
1082 } else
1083 $this->_syntax_error("expecting '=' after attribute name");
1084 break;
1085
1086 case 2:
1087 /* If token is not '=', we set the attribute value and go to
1088 state 0. */
1089 if ($token != '=') {
1090 /* We booleanize the token if it's a non-quoted possible
1091 boolean value. */
1092 if (preg_match('!^(on|yes|true)$!', $token))
1093 $token = true;
1094 else if (preg_match('!^(off|no|false)$!', $token))
1095 $token = false;
1096 /* If the token is not variable (doesn't start with
1097 '$', '#', or '%') and not enclosed in single or
1098 double quotes we single-quote it. */
1099 else if ($quote && !in_array($token{0}, $var_delims) &&
1100 !(($token{0} == '"' || $token[0] == "'") &&
1101 $token{strlen($token)-1} == $token{0}))
1102 $token = "'".$token."'";
1103
1104 $attrs[$attr_name] = $token;
1105 $state = 0;
1106 } else
1107 $this->_syntax_error("'=' cannot be an attribute value");
1108 break;
1109 }
1110 }
1111
1112 $this->_parse_vars_props($attrs);
1113
1114 return $attrs;
1115 }
1116
1117
1118 /*======================================================================*\
1119 Function: _preg_grep
1120 Purpose: Emulate PHP's preg_grep()
1121 \*======================================================================*/
1122 function _preg_grep($pattern, $array)
1123 {
1124 $result = array();
1125
1126 foreach ($array as $key => $entry) {
1127 if (preg_match($pattern, $entry))
1128 $result[$key] = $entry;
1129 }
1130
1131 return $result;
1132 }
1133
1134 function _parse_vars_props(&$tokens)
1135 {
1136 $qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
1137
1138 /* preg_grep() was fixed to return keys properly in 4.0.4 and later. To
1139 allow people to use older versions of PHP we emulate preg_grep() and
1140 use the version check to see what function to call. */
1141 if (strnatcmp(PHP_VERSION, '4.0.4') >= 0) {
1142 $var_exprs = preg_grep('!^\$(\w+/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens);
1143 $conf_var_exprs = preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens);
1144 $sect_prop_exprs = preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens);
1145 } else {
1146 $var_exprs = $this->_preg_grep('!^\$(\w+/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens);
1147 $conf_var_exprs = $this->_preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens);
1148 $sect_prop_exprs = $this->_preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens);
1149 }
1150
1151 if (count($var_exprs)) {
1152 foreach ($var_exprs as $expr_index => $var_expr) {
1153 $tokens[$expr_index] = $this->_parse_var($var_expr);
1154 }
1155 }
1156
1157 if (count($conf_var_exprs)) {
1158 foreach ($conf_var_exprs as $expr_index => $var_expr) {
1159 $tokens[$expr_index] = $this->_parse_conf_var($var_expr);
1160 }
1161 }
1162
1163 if (count($sect_prop_exprs)) {
1164 foreach ($sect_prop_exprs as $expr_index => $section_prop_expr) {
1165 $tokens[$expr_index] = $this->_parse_section_prop($section_prop_expr);
1166 }
1167 }
1168 }
1169
1170 function _parse_var($var_expr)
1171 {
1172 list($var_ref, $modifiers) = explode('|', substr($var_expr, 1), 2);
1173
1174 $sections = explode('/', $var_ref);
1175 $props = explode('.', array_pop($sections));
1176 $var_name = array_shift($props);
1177
1178 $output = "\$$var_name";
1179
1180 foreach ($sections as $section) {
1181 $output .= "[\$_sections['$section']['properties']['index']]";
1182 }
1183 foreach ($props as $prop) {
1184 $output .= "['$prop']";
1185 }
1186
1187 $this->_parse_modifiers($output, $modifiers);
1188
1189 return $output;
1190 }
1191
1192 function _parse_conf_var($conf_var_expr)
1193 {
1194 list($var_ref, $modifiers) = explode('|', $conf_var_expr, 2);
1195
1196 $var_name = substr($var_ref, 1, -1);
1197
1198 $output = "\$_config['$var_name']";
1199
1200 $this->_parse_modifiers($output, $modifiers);
1201
1202 return $output;
1203 }
1204
1205 function _parse_section_prop($section_prop_expr)
1206 {
1207 list($var_ref, $modifiers) = explode('|', $section_prop_expr, 2);
1208
1209 preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1210 $section_name = $match[1];
1211 $prop_name = $match[2];
1212
1213 $output = "\$_sections['$section_name']['properties']['$prop_name']";
1214
1215 $this->_parse_modifiers($output, $modifiers);
1216
1217 return $output;
1218 }
1219
1220 function _parse_modifiers(&$output, $modifier_string)
1221 {
1222 $qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
1223 preg_match_all('!\|(@?\w+)((?>:(?:'. $qstr_regexp . '|[^|]+))*)!', '|' . $modifier_string, $match);
1224 list(, $modifiers, $modifier_arg_strings) = $match;
1225
1226 for ($i = 0; $i < count($modifiers); $i++) {
1227 $modifier_name = $modifiers[$i];
1228 preg_match_all('!:(' . $qstr_regexp . '|[^:]+)!', $modifier_arg_strings[$i], $match);
1229 $modifier_args = $match[1];
1230
1231 if ($modifier_name{0} == '@') {
1232 $map_array = 'false';
1233 $modifier_name = substr($modifier_name, 1);
1234 } else
1235 $map_array = 'true';
1236
1237 /*
1238 * First we lookup the modifier function name in the registered
1239 * modifiers table.
1240 */
1241 $mod_func_name = $this->custom_mods[$modifier_name];
1242
1243 /*
1244 * If we don't find that modifier there, we assume it's just a PHP
1245 * function name.
1246 */
1247 if (!isset($mod_func_name))
1248 $mod_func_name = $modifier_name;
1249
1250 if (!function_exists($mod_func_name)) {
1251 $this->_syntax_error("modifier '$modifier_name' is not implemented", E_USER_WARNING);
1252 continue;
1253 }
1254
1255 $this->_parse_vars_props($modifier_args);
1256
1257 if (count($modifier_args) > 0)
1258 $modifier_args = ', '.implode(', ', $modifier_args);
1259 else
1260 $modifier_args = '';
1261
1262 $output = "_smarty_mod_handler('$mod_func_name', $map_array, $output$modifier_args)";
1263 }
1264 }
1265
1266
1267 /*======================================================================*\
1268 Function: _dequote
1269 Purpose: Remove starting and ending quotes from the string
1270 \*======================================================================*/
1271 function _dequote($string)
1272 {
1273 if (($string{0} == "'" || $string{0} == '"') &&
1274 $string{strlen($string)-1} == $string{0})
1275 return substr($string, 1, -1);
1276 else
1277 return $string;
1278 }
1279
1280
1281 /*======================================================================*\
1282 Function: _read_file()
1283 Purpose: read in a file
1284 \*======================================================================*/
1285
1286 function _read_file($filename)
1287
1288 {
1289 if (!($fd = fopen($filename, 'r'))) {
1290 $this->_set_error_msg("problem reading '$filename.'");
1291 return false;
1292 }
1293 flock($fd, LOCK_SH);
1294 $contents = fread($fd, filesize($filename));
1295 fclose($fd);
1296 return $contents;
1297 }
1298
1299 /*======================================================================*\
1300 Function: _write_file()
1301 Purpose: write out a file
1302 \*======================================================================*/
1303
1304 function _write_file($filename, $contents, $create_dirs = false)
1305 {
1306 if($create_dirs)
1307 $this->_create_dir_structure(dirname($filename));
1308
1309 if (!($fd = fopen($filename, 'a'))) {
1310 $this->_set_error_msg("problem writing '$filename.'");
1311 return false;
1312 }
1313 flock($fd, LOCK_EX);
1314
1315 $fd_safe = fopen($filename, 'w');
1316
1317 fwrite($fd_safe, $contents);
1318 fclose($fd_safe);
1319 fclose($fd);
1320
1321 return true;
1322 }
1323
1324
1325 /*======================================================================*\
1326 Function: _clear_tpl_cache_dir
1327 Purpose: Clear the specified template cache
1328 \*======================================================================*/
1329 function _clear_tpl_cache_dir($cache_tpl_md5)
1330 {
1331 $cache_dir = $this->cache_dir.'/'.$cache_tpl_md5;
1332
1333 if (!is_dir($cache_dir))
1334 return false;
1335
1336 $dir_handle = opendir($cache_dir);
1337 while ($curr_dir = readdir($dir_handle)) {
1338 $cache_path_dir = $cache_dir.'/'.$curr_dir;
1339 if ($curr_dir == '.' || $curr_dir == '..' ||
1340 !is_dir($cache_path_dir))
1341 continue;
1342
1343 $dir_handle2 = opendir($cache_path_dir);
1344 while ($curr_file = readdir($dir_handle2)) {
1345 if ($curr_file == '.' || $curr_file == '..')
1346 continue;
1347
1348 $cache_file = $cache_path_dir.'/'.$curr_file;
1349 if (is_file($cache_file))
1350 unlink($cache_file);
1351 }
1352 closedir($dir_handle2);
1353 @rmdir($cache_path_dir);
1354 }
1355 closedir($dir_handle);
1356 @rmdir($cache_dir);
1357
1358 return true;
1359 }
1360
1361
1362 /*======================================================================*\
1363 Function: _set_error_msg()
1364 Purpose: set the error message
1365 \*======================================================================*/
1366
1367 function _set_error_msg($error_msg)
1368 {
1369 $this->_error_msg="smarty error: $error_msg";
1370 return true;
1371 }
1372
1373 /*======================================================================*\
1374 Function: _syntax_error
1375 Purpose: display Smarty syntax error
1376 \*======================================================================*/
1377 function _syntax_error($error_msg, $error_type = E_USER_ERROR)
1378 {
1379 trigger_error("Smarty: [in " . $this->_current_file . " line " .
1380 $this->_current_line_no . "]: syntax error: $error_msg", $error_type);
1381 }
1382 }
1383
1384 /* vim: set expandtab: */
1385
1386 ?>

  ViewVC Help
Powered by ViewVC 1.1.26