/[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.3 - (show annotations)
Mon Mar 18 11:37:07 2002 UTC (22 years ago) by dpavlin
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +1 -0 lines
*** empty log message ***

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

  ViewVC Help
Powered by ViewVC 1.1.26