XOOPS 2.5.6  Final
 All Classes Namespaces Files Functions Variables Pages
Smarty_Compiler.class.php
Go to the documentation of this file.
1 <?php
2 
29 /* $Id: Smarty_Compiler.class.php 10263 2012-11-21 04:40:56Z beckmi $ */
30 
35 class Smarty_Compiler extends Smarty {
36 
37  // internal vars
41  var $_folded_blocks = array(); // keeps folded template blocks
42  var $_current_file = null; // the current template being compiled
43  var $_current_line_no = 1; // line number for error messages
44  var $_capture_stack = array(); // keeps track of nested capture buffers
45  var $_plugin_info = array(); // keeps track of plugins to load
46  var $_init_smarty_vars = false;
47  var $_permitted_tokens = array('true','false','yes','no','on','off','null');
48  var $_db_qstr_regexp = null; // regexps are setup in the constructor
49  var $_si_qstr_regexp = null;
50  var $_qstr_regexp = null;
51  var $_func_regexp = null;
52  var $_reg_obj_regexp = null;
54  var $_num_const_regexp = null;
55  var $_dvar_guts_regexp = null;
56  var $_dvar_regexp = null;
57  var $_cvar_regexp = null;
58  var $_svar_regexp = null;
59  var $_avar_regexp = null;
60  var $_mod_regexp = null;
61  var $_var_regexp = null;
63  var $_func_call_regexp = null;
64  var $_obj_ext_regexp = null;
65  var $_obj_start_regexp = null;
66  var $_obj_params_regexp = null;
67  var $_obj_call_regexp = null;
70  var $_nocache_count = 0;
71  var $_cache_serial = null;
72  var $_cache_include = null;
73 
74  var $_strip_depth = 0;
76 
81  function Smarty_Compiler()
82  {
83  // matches double quoted strings:
84  // "foobar"
85  // "foo\"bar"
86  $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
87 
88  // matches single quoted strings:
89  // 'foobar'
90  // 'foo\'bar'
91  $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
92 
93  // matches single or double quoted strings
94  $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
95 
96  // matches bracket portion of vars
97  // [0]
98  // [foo]
99  // [$bar]
100  $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
101 
102  // matches numerical constants
103  // 30
104  // -12
105  // 13.22
106  $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
107 
108  // matches $ vars (not objects):
109  // $foo
110  // $foo.bar
111  // $foo.bar.foobar
112  // $foo[0]
113  // $foo[$bar]
114  // $foo[5][blah]
115  // $foo[5].bar[$foobar][4]
116  $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))';
117  $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d>\[\]]';
118  $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
119  . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
120  $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
121 
122  // matches config vars:
123  // #foo#
124  // #foobar123_foo#
125  $this->_cvar_regexp = '\#\w+\#';
126 
127  // matches section vars:
128  // %foo.bar%
129  $this->_svar_regexp = '\%\w+\.\w+\%';
130 
131  // matches all valid variables (no quotes, no modifiers)
132  $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
133  . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
134 
135  // matches valid variable syntax:
136  // $foo
137  // $foo
138  // #foo#
139  // #foo#
140  // "text"
141  // "text"
142  $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
143 
144  // matches valid object call (one level of object nesting allowed in parameters):
145  // $foo->bar
146  // $foo->bar()
147  // $foo->bar("text")
148  // $foo->bar($foo, $bar, "text")
149  // $foo->bar($foo, "foo")
150  // $foo->bar->foo()
151  // $foo->bar->foo->bar()
152  // $foo->bar($foo->bar)
153  // $foo->bar($foo->bar())
154  // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
155  $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
156  $this->_obj_restricted_param_regexp = '(?:'
157  . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')'
158  . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)';
159  $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
160  . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
161  $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
162  . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
163  $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
164  $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?)';
165 
166  // matches valid modifier syntax:
167  // |foo
168  // |@foo
169  // |foo:"bar"
170  // |foo:$bar
171  // |foo:"bar":$foobar
172  // |foo|bar
173  // |foo:$foo->bar
174  $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|'
175  . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
176 
177  // matches valid function name:
178  // foo123
179  // _foo_bar
180  $this->_func_regexp = '[a-zA-Z_]\w*';
181 
182  // matches valid registered object:
183  // foo->bar
184  $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
185 
186  // matches valid parameter values:
187  // true
188  // $foo
189  // $foo|bar
190  // #foo#
191  // #foo#|bar
192  // "text"
193  // "text"|bar
194  // $foo->bar
195  $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
196  . $this->_var_regexp . '|' . $this->_num_const_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
197 
198  // matches valid parenthesised function parameters:
199  //
200  // "text"
201  // $foo, $bar, "text"
202  // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
203  $this->_parenth_param_regexp = '(?:\((?:\w+|'
204  . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
205  . $this->_param_regexp . ')))*)?\))';
206 
207  // matches valid function call:
208  // foo()
209  // foo_bar($foo)
210  // _foo_bar($foo,"bar")
211  // foo123($foo,$foo->bar(),"foo")
212  $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
213  . $this->_parenth_param_regexp . '))';
214  }
215 
225  function _compile_file($resource_name, $source_content, &$compiled_content)
226  {
227 
228  if ($this->security) {
229  // do not allow php syntax to be executed unless specified
230  if ($this->php_handling == SMARTY_PHP_ALLOW &&
231  !$this->security_settings['PHP_HANDLING']) {
232  $this->php_handling = SMARTY_PHP_PASSTHRU;
233  }
234  }
235 
236  $this->_load_filters();
237 
238  $this->_current_file = $resource_name;
239  $this->_current_line_no = 1;
240  $ldq = preg_quote($this->left_delimiter, '~');
241  $rdq = preg_quote($this->right_delimiter, '~');
242 
243  // run template source through prefilter functions
244  if (count($this->_plugins['prefilter']) > 0) {
245  foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
246  if ($prefilter === false) continue;
247  if ($prefilter[3] || is_callable($prefilter[0])) {
248  $source_content = call_user_func_array($prefilter[0],
249  array($source_content, &$this));
250  $this->_plugins['prefilter'][$filter_name][3] = true;
251  } else {
252  $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
253  }
254  }
255  }
256 
257  /* fetch all special blocks */
258  $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
259 
260  preg_match_all($search, $source_content, $match, PREG_SET_ORDER);
261  $this->_folded_blocks = $match;
262  reset($this->_folded_blocks);
263 
264  /* replace special blocks by "{php}" */
265  $source_content = preg_replace($search.'e', "'"
266  . $this->_quote_replace($this->left_delimiter) . 'php'
267  . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
268  . $this->_quote_replace($this->right_delimiter)
269  . "'"
270  , $source_content);
271 
272  /* Gather all template tags. */
273  preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match);
274  $template_tags = $_match[1];
275  /* Split content by template tags to obtain non-template content. */
276  $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);
277 
278  /* loop through text blocks */
279  for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
280  /* match anything resembling php tags */
281  if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?\s*php\s*[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) {
282  /* replace tags with placeholders to prevent recursive replacements */
283  $sp_match[1] = array_unique($sp_match[1]);
284  usort($sp_match[1], '_smarty_sort_length');
285  for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
286  $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
287  }
288  /* process each one */
289  for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
290  if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
291  /* echo php contents */
292  $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
293  } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
294  /* quote php tags */
295  $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
296  } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
297  /* remove php tags */
298  $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
299  } else {
300  /* SMARTY_PHP_ALLOW, but echo non php starting tags */
301  $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
302  $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
303  }
304  }
305  }
306  }
307 
308  /* Compile the template tags into PHP code. */
309  $compiled_tags = array();
310  for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
311  $this->_current_line_no += substr_count($text_blocks[$i], "\n");
312  $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
313  $this->_current_line_no += substr_count($template_tags[$i], "\n");
314  }
315  if (count($this->_tag_stack)>0) {
316  list($_open_tag, $_line_no) = end($this->_tag_stack);
317  $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
318  return;
319  }
320 
321  /* Reformat $text_blocks between 'strip' and '/strip' tags,
322  removing spaces, tabs and newlines. */
323  $strip = false;
324  for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
325  if ($compiled_tags[$i] == '{strip}') {
326  $compiled_tags[$i] = '';
327  $strip = true;
328  /* remove leading whitespaces */
329  $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
330  }
331  if ($strip) {
332  /* strip all $text_blocks before the next '/strip' */
333  for ($j = $i + 1; $j < $for_max; $j++) {
334  /* remove leading and trailing whitespaces of each line */
335  $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]);
336  if ($compiled_tags[$j] == '{/strip}') {
337  /* remove trailing whitespaces from the last text_block */
338  $text_blocks[$j] = rtrim($text_blocks[$j]);
339  }
340  $text_blocks[$j] = "<?php echo '" . strtr($text_blocks[$j], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>";
341  if ($compiled_tags[$j] == '{/strip}') {
342  $compiled_tags[$j] = "\n"; /* slurped by php, but necessary
343  if a newline is following the closing strip-tag */
344  $strip = false;
345  $i = $j;
346  break;
347  }
348  }
349  }
350  }
351  $compiled_content = '';
352 
353  $tag_guard = '%%%SMARTYOTG' . md5(uniqid(rand(), true)) . '%%%';
354 
355  /* Interleave the compiled contents and text blocks to get the final result. */
356  for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
357  if ($compiled_tags[$i] == '') {
358  // tag result empty, remove first newline from following text block
359  $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]);
360  }
361  // replace legit PHP tags with placeholder
362  $text_blocks[$i] = str_replace('<?', $tag_guard, $text_blocks[$i]);
363  $compiled_tags[$i] = str_replace('<?', $tag_guard, $compiled_tags[$i]);
364 
365  $compiled_content .= $text_blocks[$i] . $compiled_tags[$i];
366  }
367  $compiled_content .= str_replace('<?', $tag_guard, $text_blocks[$i]);
368 
369  // escape php tags created by interleaving
370  $compiled_content = str_replace('<?', "<?php echo '<?' ?>\n", $compiled_content);
371  $compiled_content = preg_replace("~(?<!')language\s*=\s*[\"\']?\s*php\s*[\"\']?~", "<?php echo 'language=php' ?>\n", $compiled_content);
372 
373  // recover legit tags
374  $compiled_content = str_replace($tag_guard, '<?', $compiled_content);
375 
376  // remove \n from the end of the file, if any
377  if (strlen($compiled_content) && (substr($compiled_content, -1) == "\n") ) {
378  $compiled_content = substr($compiled_content, 0, -1);
379  }
380 
381  if (!empty($this->_cache_serial)) {
382  $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
383  }
384 
385  // run compiled template through postfilter functions
386  if (count($this->_plugins['postfilter']) > 0) {
387  foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
388  if ($postfilter === false) continue;
389  if ($postfilter[3] || is_callable($postfilter[0])) {
390  $compiled_content = call_user_func_array($postfilter[0],
391  array($compiled_content, &$this));
392  $this->_plugins['postfilter'][$filter_name][3] = true;
393  } else {
394  $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
395  }
396  }
397  }
398 
399  // put header at the top of the compiled template
400  $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
401  $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
402 
403  /* Emit code to load needed plugins. */
404  $this->_plugins_code = '';
405  if (count($this->_plugin_info)) {
406  $_plugins_params = "array('plugins' => array(";
407  foreach ($this->_plugin_info as $plugin_type => $plugins) {
408  foreach ($plugins as $plugin_name => $plugin_info) {
409  $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
410  $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
411  }
412  }
413  $_plugins_params .= '))';
414  $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
415  $template_header .= $plugins_code;
416  $this->_plugin_info = array();
417  $this->_plugins_code = $plugins_code;
418  }
419 
420  if ($this->_init_smarty_vars) {
421  $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
422  $this->_init_smarty_vars = false;
423  }
424 
425  $compiled_content = $template_header . $compiled_content;
426  return true;
427  }
428 
435  function _compile_tag($template_tag)
436  {
437  /* Matched comment. */
438  if (substr($template_tag, 0, 1) == '*' && substr($template_tag, -1) == '*')
439  return '';
440 
441  /* Split tag into two three parts: command, command modifiers and the arguments. */
442  if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp
443  . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
444  (?:\s+(.*))?$
445  ~xs', $template_tag, $match)) {
446  $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
447  }
448 
449  $tag_command = $match[1];
450  $tag_modifier = isset($match[2]) ? $match[2] : null;
451  $tag_args = isset($match[3]) ? $match[3] : null;
452 
453  if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {
454  /* tag name is a variable or object */
455  $_return = $this->_parse_var_props($tag_command . $tag_modifier);
456  return "<?php echo $_return; ?>" . $this->_additional_newline;
457  }
458 
459  /* If the tag name is a registered object, we process it. */
460  if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) {
461  return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
462  }
463 
464  switch ($tag_command) {
465  case 'include':
466  return $this->_compile_include_tag($tag_args);
467 
468  case 'include_php':
469  return $this->_compile_include_php_tag($tag_args);
470 
471  case 'if':
472  $this->_push_tag('if');
473  return $this->_compile_if_tag($tag_args);
474 
475  case 'else':
476  list($_open_tag) = end($this->_tag_stack);
477  if ($_open_tag != 'if' && $_open_tag != 'elseif')
478  $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
479  else
480  $this->_push_tag('else');
481  return '<?php else: ?>';
482 
483  case 'elseif':
484  list($_open_tag) = end($this->_tag_stack);
485  if ($_open_tag != 'if' && $_open_tag != 'elseif')
486  $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
487  if ($_open_tag == 'if')
488  $this->_push_tag('elseif');
489  return $this->_compile_if_tag($tag_args, true);
490 
491  case '/if':
492  $this->_pop_tag('if');
493  return '<?php endif; ?>';
494 
495  case 'capture':
496  return $this->_compile_capture_tag(true, $tag_args);
497 
498  case '/capture':
499  return $this->_compile_capture_tag(false);
500 
501  case 'ldelim':
502  return $this->left_delimiter;
503 
504  case 'rdelim':
505  return $this->right_delimiter;
506 
507  case 'section':
508  $this->_push_tag('section');
509  return $this->_compile_section_start($tag_args);
510 
511  case 'sectionelse':
512  $this->_push_tag('sectionelse');
513  return "<?php endfor; else: ?>";
514  break;
515 
516  case '/section':
517  $_open_tag = $this->_pop_tag('section');
518  if ($_open_tag == 'sectionelse')
519  return "<?php endif; ?>";
520  else
521  return "<?php endfor; endif; ?>";
522 
523  case 'foreach':
524  $this->_push_tag('foreach');
525  return $this->_compile_foreach_start($tag_args);
526  break;
527 
528  case 'foreachelse':
529  $this->_push_tag('foreachelse');
530  return "<?php endforeach; else: ?>";
531 
532  case '/foreach':
533  $_open_tag = $this->_pop_tag('foreach');
534  if ($_open_tag == 'foreachelse')
535  return "<?php endif; unset(\$_from); ?>";
536  else
537  return "<?php endforeach; endif; unset(\$_from); ?>";
538  break;
539 
540  case 'strip':
541  case '/strip':
542  if (substr($tag_command, 0, 1)=='/') {
543  $this->_pop_tag('strip');
544  if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
545  $this->_additional_newline = "\n";
546  return '{' . $tag_command . '}';
547  }
548  } else {
549  $this->_push_tag('strip');
550  if ($this->_strip_depth++==0) { /* outermost opening {strip} */
551  $this->_additional_newline = "";
552  return '{' . $tag_command . '}';
553  }
554  }
555  return '';
556 
557  case 'php':
558  /* handle folded tags replaced by {php} */
559  list(, $block) = each($this->_folded_blocks);
560  $this->_current_line_no += substr_count($block[0], "\n");
561  /* the number of matched elements in the regexp in _compile_file()
562  determins the type of folded tag that was found */
563  switch (count($block)) {
564  case 2: /* comment */
565  return '';
566 
567  case 3: /* literal */
568  return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
569 
570  case 4: /* php */
571  if ($this->security && !$this->security_settings['PHP_TAGS']) {
572  $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
573  return;
574  }
575  return '<?php ' . $block[3] .' ?>';
576  }
577  break;
578 
579  case 'insert':
580  return $this->_compile_insert_tag($tag_args);
581 
582  default:
583  if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
584  return $output;
585  } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
586  return $output;
587  } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
588  return $output;
589  } else {
590  $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
591  }
592 
593  }
594  }
595 
596 
606  function _compile_compiler_tag($tag_command, $tag_args, &$output)
607  {
608  $found = false;
609  $have_function = true;
610 
611  /*
612  * First we check if the compiler function has already been registered
613  * or loaded from a plugin file.
614  */
615  if (isset($this->_plugins['compiler'][$tag_command])) {
616  $found = true;
617  $plugin_func = $this->_plugins['compiler'][$tag_command][0];
618  if (!is_callable($plugin_func)) {
619  $message = "compiler function '$tag_command' is not implemented";
620  $have_function = false;
621  }
622  }
623  /*
624  * Otherwise we need to load plugin file and look for the function
625  * inside it.
626  */
627  else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
628  $found = true;
629 
630  include_once $plugin_file;
631 
632  $plugin_func = 'smarty_compiler_' . $tag_command;
633  if (!is_callable($plugin_func)) {
634  $message = "plugin function $plugin_func() not found in $plugin_file\n";
635  $have_function = false;
636  } else {
637  $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
638  }
639  }
640 
641  /*
642  * True return value means that we either found a plugin or a
643  * dynamically registered function. False means that we didn't and the
644  * compiler should now emit code to load custom function plugin for this
645  * tag.
646  */
647  if ($found) {
648  if ($have_function) {
649  $output = call_user_func_array($plugin_func, array($tag_args, &$this));
650  if($output != '') {
651  $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
652  . $output
653  . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
654  }
655  } else {
656  $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
657  }
658  return true;
659  } else {
660  return false;
661  }
662  }
663 
664 
675  function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
676  {
677  if (substr($tag_command, 0, 1) == '/') {
678  $start_tag = false;
679  $tag_command = substr($tag_command, 1);
680  } else
681  $start_tag = true;
682 
683  $found = false;
684  $have_function = true;
685 
686  /*
687  * First we check if the block function has already been registered
688  * or loaded from a plugin file.
689  */
690  if (isset($this->_plugins['block'][$tag_command])) {
691  $found = true;
692  $plugin_func = $this->_plugins['block'][$tag_command][0];
693  if (!is_callable($plugin_func)) {
694  $message = "block function '$tag_command' is not implemented";
695  $have_function = false;
696  }
697  }
698  /*
699  * Otherwise we need to load plugin file and look for the function
700  * inside it.
701  */
702  else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
703  $found = true;
704 
705  include_once $plugin_file;
706 
707  $plugin_func = 'smarty_block_' . $tag_command;
708  if (!function_exists($plugin_func)) {
709  $message = "plugin function $plugin_func() not found in $plugin_file\n";
710  $have_function = false;
711  } else {
712  $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
713 
714  }
715  }
716 
717  if (!$found) {
718  return false;
719  } else if (!$have_function) {
720  $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
721  return true;
722  }
723 
724  /*
725  * Even though we've located the plugin function, compilation
726  * happens only once, so the plugin will still need to be loaded
727  * at runtime for future requests.
728  */
729  $this->_add_plugin('block', $tag_command);
730 
731  if ($start_tag)
732  $this->_push_tag($tag_command);
733  else
734  $this->_pop_tag($tag_command);
735 
736  if ($start_tag) {
737  $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
738  $attrs = $this->_parse_attrs($tag_args);
739  $_cache_attrs='';
740  $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs);
741  $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
742  $output .= '$_block_repeat=true;' . $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat);';
743  $output .= 'while ($_block_repeat) { ob_start(); ?>';
744  } else {
745  $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); ';
746  $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat)';
747  if ($tag_modifier != '') {
748  $this->_parse_modifiers($_out_tag_text, $tag_modifier);
749  }
750  $output .= '$_block_repeat=false;echo ' . $_out_tag_text . '; } ';
751  $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
752  }
753 
754  return true;
755  }
756 
757 
766  function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
767  {
768  $found = false;
769  $have_function = true;
770 
771  /*
772  * First we check if the custom function has already been registered
773  * or loaded from a plugin file.
774  */
775  if (isset($this->_plugins['function'][$tag_command])) {
776  $found = true;
777  $plugin_func = $this->_plugins['function'][$tag_command][0];
778  if (!is_callable($plugin_func)) {
779  $message = "custom function '$tag_command' is not implemented";
780  $have_function = false;
781  }
782  }
783  /*
784  * Otherwise we need to load plugin file and look for the function
785  * inside it.
786  */
787  else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
788  $found = true;
789 
790  include_once $plugin_file;
791 
792  $plugin_func = 'smarty_function_' . $tag_command;
793  if (!function_exists($plugin_func)) {
794  $message = "plugin function $plugin_func() not found in $plugin_file\n";
795  $have_function = false;
796  } else {
797  $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true);
798 
799  }
800  }
801 
802  if (!$found) {
803  return false;
804  } else if (!$have_function) {
805  $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
806  return true;
807  }
808 
809  /* declare plugin to be loaded on display of the template that
810  we compile right now */
811  $this->_add_plugin('function', $tag_command);
812 
813  $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
814  $attrs = $this->_parse_attrs($tag_args);
815  $_cache_attrs = '';
816  $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs);
817 
818  $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
819  if($tag_modifier != '') {
820  $this->_parse_modifiers($output, $tag_modifier);
821  }
822 
823  if($output != '') {
824  $output = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
825  . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
826  }
827 
828  return true;
829  }
830 
839  function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
840  {
841  if (substr($tag_command, 0, 1) == '/') {
842  $start_tag = false;
843  $tag_command = substr($tag_command, 1);
844  } else {
845  $start_tag = true;
846  }
847 
848  list($object, $obj_comp) = explode('->', $tag_command);
849 
850  $arg_list = array();
851  if(count($attrs)) {
852  $_assign_var = false;
853  foreach ($attrs as $arg_name => $arg_value) {
854  if($arg_name == 'assign') {
855  $_assign_var = $arg_value;
856  unset($attrs['assign']);
857  continue;
858  }
859  if (is_bool($arg_value))
860  $arg_value = $arg_value ? 'true' : 'false';
861  $arg_list[] = "'$arg_name' => $arg_value";
862  }
863  }
864 
865  if($this->_reg_objects[$object][2]) {
866  // smarty object argument format
867  $args = "array(".implode(',', (array)$arg_list)."), \$this";
868  } else {
869  // traditional argument format
870  $args = implode(',', array_values($attrs));
871  if (empty($args)) {
872  $args = '';
873  }
874  }
875 
876  $prefix = '';
877  $postfix = '';
878  $newline = '';
879  if(!is_object($this->_reg_objects[$object][0])) {
880  $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
881  } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
882  $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
883  } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
884  // method
885  if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
886  // block method
887  if ($start_tag) {
888  $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
889  $prefix .= "\$_block_repeat=true; \$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat); ";
890  $prefix .= "while (\$_block_repeat) { ob_start();";
891  $return = null;
892  $postfix = '';
893  } else {
894  $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); \$_block_repeat=false;";
895  $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat)";
896  $postfix = "} array_pop(\$this->_tag_stack);";
897  }
898  } else {
899  // non-block method
900  $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
901  }
902  } else {
903  // property
904  $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
905  }
906 
907  if($return != null) {
908  if($tag_modifier != '') {
909  $this->_parse_modifiers($return, $tag_modifier);
910  }
911 
912  if(!empty($_assign_var)) {
913  $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);";
914  } else {
915  $output = 'echo ' . $return . ';';
916  $newline = $this->_additional_newline;
917  }
918  } else {
919  $output = '';
920  }
921 
922  return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
923  }
924 
931  function _compile_insert_tag($tag_args)
932  {
933  $attrs = $this->_parse_attrs($tag_args);
934  $name = $this->_dequote($attrs['name']);
935 
936  if (empty($name)) {
937  return $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
938  }
939 
940  if (!preg_match('~^\w+$~', $name)) {
941  return $this->_syntax_error("'insert: 'name' must be an insert function name", E_USER_ERROR, __FILE__, __LINE__);
942  }
943 
944  if (!empty($attrs['script'])) {
945  $delayed_loading = true;
946  } else {
947  $delayed_loading = false;
948  }
949 
950  foreach ($attrs as $arg_name => $arg_value) {
951  if (is_bool($arg_value))
952  $arg_value = $arg_value ? 'true' : 'false';
953  $arg_list[] = "'$arg_name' => $arg_value";
954  }
955 
956  $this->_add_plugin('insert', $name, $delayed_loading);
957 
958  $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
959 
960  return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
961  }
962 
969  function _compile_include_tag($tag_args)
970  {
971  $attrs = $this->_parse_attrs($tag_args);
972  $arg_list = array();
973 
974  if (empty($attrs['file'])) {
975  $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
976  }
977 
978  foreach ($attrs as $arg_name => $arg_value) {
979  if ($arg_name == 'file') {
980  $include_file = $arg_value;
981  continue;
982  } else if ($arg_name == 'assign') {
983  $assign_var = $arg_value;
984  continue;
985  }
986  if (is_bool($arg_value))
987  $arg_value = $arg_value ? 'true' : 'false';
988  $arg_list[] = "'$arg_name' => $arg_value";
989  }
990 
991  $output = '<?php ';
992 
993  if (isset($assign_var)) {
994  $output .= "ob_start();\n";
995  }
996 
997  $output .=
998  "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
999 
1000 
1001  $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
1002  $output .= "\$this->_smarty_include($_params);\n" .
1003  "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
1004  "unset(\$_smarty_tpl_vars);\n";
1005 
1006  if (isset($assign_var)) {
1007  $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
1008  }
1009 
1010  $output .= ' ?>';
1011 
1012  return $output;
1013 
1014  }
1015 
1022  function _compile_include_php_tag($tag_args)
1023  {
1024  $attrs = $this->_parse_attrs($tag_args);
1025 
1026  if (empty($attrs['file'])) {
1027  $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
1028  }
1029 
1030  $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
1031  $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
1032 
1033  $arg_list = array();
1034  foreach($attrs as $arg_name => $arg_value) {
1035  if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
1036  if(is_bool($arg_value))
1037  $arg_value = $arg_value ? 'true' : 'false';
1038  $arg_list[] = "'$arg_name' => $arg_value";
1039  }
1040  }
1041 
1042  $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
1043 
1044  return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
1045  }
1046 
1047 
1054  function _compile_section_start($tag_args)
1055  {
1056  $attrs = $this->_parse_attrs($tag_args);
1057  $arg_list = array();
1058 
1059  $output = '<?php ';
1060  $section_name = $attrs['name'];
1061  if (empty($section_name)) {
1062  $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
1063  }
1064 
1065  $output .= "unset(\$this->_sections[$section_name]);\n";
1066  $section_props = "\$this->_sections[$section_name]";
1067 
1068  foreach ($attrs as $attr_name => $attr_value) {
1069  switch ($attr_name) {
1070  case 'loop':
1071  $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
1072  break;
1073 
1074  case 'show':
1075  if (is_bool($attr_value))
1076  $show_attr_value = $attr_value ? 'true' : 'false';
1077  else
1078  $show_attr_value = "(bool)$attr_value";
1079  $output .= "{$section_props}['show'] = $show_attr_value;\n";
1080  break;
1081 
1082  case 'name':
1083  $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
1084  break;
1085 
1086  case 'max':
1087  case 'start':
1088  $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
1089  break;
1090 
1091  case 'step':
1092  $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
1093  break;
1094 
1095  default:
1096  $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
1097  break;
1098  }
1099  }
1100 
1101  if (!isset($attrs['show']))
1102  $output .= "{$section_props}['show'] = true;\n";
1103 
1104  if (!isset($attrs['loop']))
1105  $output .= "{$section_props}['loop'] = 1;\n";
1106 
1107  if (!isset($attrs['max']))
1108  $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
1109  else
1110  $output .= "if ({$section_props}['max'] < 0)\n" .
1111  " {$section_props}['max'] = {$section_props}['loop'];\n";
1112 
1113  if (!isset($attrs['step']))
1114  $output .= "{$section_props}['step'] = 1;\n";
1115 
1116  if (!isset($attrs['start']))
1117  $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
1118  else {
1119  $output .= "if ({$section_props}['start'] < 0)\n" .
1120  " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
1121  "else\n" .
1122  " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
1123  }
1124 
1125  $output .= "if ({$section_props}['show']) {\n";
1126  if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
1127  $output .= " {$section_props}['total'] = {$section_props}['loop'];\n";
1128  } else {
1129  $output .= " {$section_props}['total'] = min(ceil(({$section_props}['step'] > 0 ? {$section_props}['loop'] - {$section_props}['start'] : {$section_props}['start']+1)/abs({$section_props}['step'])), {$section_props}['max']);\n";
1130  }
1131  $output .= " if ({$section_props}['total'] == 0)\n" .
1132  " {$section_props}['show'] = false;\n" .
1133  "} else\n" .
1134  " {$section_props}['total'] = 0;\n";
1135 
1136  $output .= "if ({$section_props}['show']):\n";
1137  $output .= "
1138  for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
1139  {$section_props}['iteration'] <= {$section_props}['total'];
1140  {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
1141  $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
1142  $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
1143  $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
1144  $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n";
1145  $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n";
1146 
1147  $output .= "?>";
1148 
1149  return $output;
1150  }
1151 
1152 
1159  function _compile_foreach_start($tag_args)
1160  {
1161  $attrs = $this->_parse_attrs($tag_args);
1162  $arg_list = array();
1163 
1164  if (empty($attrs['from'])) {
1165  return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
1166  }
1167  $from = $attrs['from'];
1168 
1169  if (empty($attrs['item'])) {
1170  return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
1171  }
1172  $item = $this->_dequote($attrs['item']);
1173  if (!preg_match('~^\w+$~', $item)) {
1174  return $this->_syntax_error("foreach: 'item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1175  }
1176 
1177  if (isset($attrs['key'])) {
1178  $key = $this->_dequote($attrs['key']);
1179  if (!preg_match('~^\w+$~', $key)) {
1180  return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1181  }
1182  $key_part = "\$this->_tpl_vars['$key'] => ";
1183  } else {
1184  $key = null;
1185  $key_part = '';
1186  }
1187 
1188  if (isset($attrs['name'])) {
1189  $name = $attrs['name'];
1190  } else {
1191  $name = null;
1192  }
1193 
1194  $output = '<?php ';
1195  $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
1196  if (isset($name)) {
1197  $foreach_props = "\$this->_foreach[$name]";
1198  $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
1199  $output .= "if ({$foreach_props}['total'] > 0):\n";
1200  $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1201  $output .= " {$foreach_props}['iteration']++;\n";
1202  } else {
1203  $output .= "if (count(\$_from)):\n";
1204  $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1205  }
1206  $output .= '?>';
1207 
1208  return $output;
1209  }
1210 
1211 
1220  function _compile_capture_tag($start, $tag_args = '')
1221  {
1222  $attrs = $this->_parse_attrs($tag_args);
1223 
1224  if ($start) {
1225  $buffer = isset($attrs['name']) ? $attrs['name'] : "'default'";
1226  $assign = isset($attrs['assign']) ? $attrs['assign'] : null;
1227  $append = isset($attrs['append']) ? $attrs['append'] : null;
1228 
1229  $output = "<?php ob_start(); ?>";
1230  $this->_capture_stack[] = array($buffer, $assign, $append);
1231  } else {
1232  list($buffer, $assign, $append) = array_pop($this->_capture_stack);
1233  $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
1234  if (isset($assign)) {
1235  $output .= " \$this->assign($assign, ob_get_contents());";
1236  }
1237  if (isset($append)) {
1238  $output .= " \$this->append($append, ob_get_contents());";
1239  }
1240  $output .= "ob_end_clean(); ?>";
1241  }
1242 
1243  return $output;
1244  }
1245 
1253  function _compile_if_tag($tag_args, $elseif = false)
1254  {
1255 
1256  /* Tokenize args for 'if' tag. */
1257  preg_match_all('~(?>
1258  ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
1259  ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)? | # var or quoted string
1260  \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token
1261  \b\w+\b | # valid word token
1262  \S+ # anything else
1263  )~x', $tag_args, $match);
1264 
1265  $tokens = $match[0];
1266 
1267  if(empty($tokens)) {
1268  $_error_msg = $elseif ? "'elseif'" : "'if'";
1269  $_error_msg .= ' statement requires arguments';
1270  $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__);
1271  }
1272 
1273 
1274  // make sure we have balanced parenthesis
1275  $token_count = array_count_values($tokens);
1276  if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1277  $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
1278  }
1279 
1280  $is_arg_stack = array();
1281 
1282  for ($i = 0; $i < count($tokens); $i++) {
1283 
1284  $token = &$tokens[$i];
1285 
1286  switch (strtolower($token)) {
1287  case '!':
1288  case '%':
1289  case '!==':
1290  case '==':
1291  case '===':
1292  case '>':
1293  case '<':
1294  case '!=':
1295  case '<>':
1296  case '<<':
1297  case '>>':
1298  case '<=':
1299  case '>=':
1300  case '&&':
1301  case '||':
1302  case '|':
1303  case '^':
1304  case '&':
1305  case '~':
1306  case ')':
1307  case ',':
1308  case '+':
1309  case '-':
1310  case '*':
1311  case '/':
1312  case '@':
1313  break;
1314 
1315  case 'eq':
1316  $token = '==';
1317  break;
1318 
1319  case 'ne':
1320  case 'neq':
1321  $token = '!=';
1322  break;
1323 
1324  case 'lt':
1325  $token = '<';
1326  break;
1327 
1328  case 'le':
1329  case 'lte':
1330  $token = '<=';
1331  break;
1332 
1333  case 'gt':
1334  $token = '>';
1335  break;
1336 
1337  case 'ge':
1338  case 'gte':
1339  $token = '>=';
1340  break;
1341 
1342  case 'and':
1343  $token = '&&';
1344  break;
1345 
1346  case 'or':
1347  $token = '||';
1348  break;
1349 
1350  case 'not':
1351  $token = '!';
1352  break;
1353 
1354  case 'mod':
1355  $token = '%';
1356  break;
1357 
1358  case '(':
1359  array_push($is_arg_stack, $i);
1360  break;
1361 
1362  case 'is':
1363  /* If last token was a ')', we operate on the parenthesized
1364  expression. The start of the expression is on the stack.
1365  Otherwise, we operate on the last encountered token. */
1366  if ($tokens[$i-1] == ')') {
1367  $is_arg_start = array_pop($is_arg_stack);
1368  if ($is_arg_start != 0) {
1369  if (preg_match('~^' . $this->_func_regexp . '$~', $tokens[$is_arg_start-1])) {
1370  $is_arg_start--;
1371  }
1372  }
1373  } else
1374  $is_arg_start = $i-1;
1375  /* Construct the argument for 'is' expression, so it knows
1376  what to operate on. */
1377  $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1378 
1379  /* Pass all tokens from next one until the end to the
1380  'is' expression parsing function. The function will
1381  return modified tokens, where the first one is the result
1382  of the 'is' expression and the rest are the tokens it
1383  didn't touch. */
1384  $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
1385 
1386  /* Replace the old tokens with the new ones. */
1387  array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1388 
1389  /* Adjust argument start so that it won't change from the
1390  current position for the next iteration. */
1391  $i = $is_arg_start;
1392  break;
1393 
1394  default:
1395  if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) {
1396  // function call
1397  if($this->security &&
1398  !in_array($token, $this->security_settings['IF_FUNCS'])) {
1399  $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1400  }
1401  } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && (strpos('+-*/^%&|', substr($token, -1)) === false) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
1402  // variable function call
1403  $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1404  } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {
1405  // object or variable
1406  $token = $this->_parse_var_props($token);
1407  } elseif(is_numeric($token)) {
1408  // number, skip it
1409  } else {
1410  $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
1411  }
1412  break;
1413  }
1414  }
1415 
1416  if ($elseif)
1417  return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1418  else
1419  return '<?php if ('.implode(' ', $tokens).'): ?>';
1420  }
1421 
1422 
1423  function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1424  $arg_list = array();
1425 
1426  if (isset($type) && isset($name)
1427  && isset($this->_plugins[$type])
1428  && isset($this->_plugins[$type][$name])
1429  && empty($this->_plugins[$type][$name][4])
1430  && is_array($this->_plugins[$type][$name][5])
1431  ) {
1432  /* we have a list of parameters that should be cached */
1433  $_cache_attrs = $this->_plugins[$type][$name][5];
1434  $_count = $this->_cache_attrs_count++;
1435  $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1436 
1437  } else {
1438  /* no parameters are cached */
1439  $_cache_attrs = null;
1440  }
1441 
1442  foreach ($attrs as $arg_name => $arg_value) {
1443  if (is_bool($arg_value))
1444  $arg_value = $arg_value ? 'true' : 'false';
1445  if (is_null($arg_value))
1446  $arg_value = 'null';
1447  if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1448  $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1449  } else {
1450  $arg_list[] = "'$arg_name' => $arg_value";
1451  }
1452  }
1453  return $arg_list;
1454  }
1455 
1463  function _parse_is_expr($is_arg, $tokens)
1464  {
1465  $expr_end = 0;
1466  $negate_expr = false;
1467 
1468  if (($first_token = array_shift($tokens)) == 'not') {
1469  $negate_expr = true;
1470  $expr_type = array_shift($tokens);
1471  } else
1472  $expr_type = $first_token;
1473 
1474  switch ($expr_type) {
1475  case 'even':
1476  if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1477  $expr_end++;
1478  $expr_arg = $tokens[$expr_end++];
1479  $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1480  } else
1481  $expr = "!(1 & $is_arg)";
1482  break;
1483 
1484  case 'odd':
1485  if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1486  $expr_end++;
1487  $expr_arg = $tokens[$expr_end++];
1488  $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1489  } else
1490  $expr = "(1 & $is_arg)";
1491  break;
1492 
1493  case 'div':
1494  if (@$tokens[$expr_end] == 'by') {
1495  $expr_end++;
1496  $expr_arg = $tokens[$expr_end++];
1497  $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1498  } else {
1499  $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1500  }
1501  break;
1502 
1503  default:
1504  $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1505  break;
1506  }
1507 
1508  if ($negate_expr) {
1509  $expr = "!($expr)";
1510  }
1511 
1512  array_splice($tokens, 0, $expr_end, $expr);
1513 
1514  return $tokens;
1515  }
1516 
1517 
1524  function _parse_attrs($tag_args)
1525  {
1526 
1527  /* Tokenize tag attributes. */
1528  preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
1529  )+ |
1530  [=]
1531  ~x', $tag_args, $match);
1532  $tokens = $match[0];
1533 
1534  $attrs = array();
1535  /* Parse state:
1536  0 - expecting attribute name
1537  1 - expecting '='
1538  2 - expecting attribute value (not '=') */
1539  $state = 0;
1540 
1541  foreach ($tokens as $token) {
1542  switch ($state) {
1543  case 0:
1544  /* If the token is a valid identifier, we set attribute name
1545  and go to state 1. */
1546  if (preg_match('~^\w+$~', $token)) {
1547  $attr_name = $token;
1548  $state = 1;
1549  } else
1550  $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1551  break;
1552 
1553  case 1:
1554  /* If the token is '=', then we go to state 2. */
1555  if ($token == '=') {
1556  $state = 2;
1557  } else
1558  $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1559  break;
1560 
1561  case 2:
1562  /* If token is not '=', we set the attribute value and go to
1563  state 0. */
1564  if ($token != '=') {
1565  /* We booleanize the token if it's a non-quoted possible
1566  boolean value. */
1567  if (preg_match('~^(on|yes|true)$~', $token)) {
1568  $token = 'true';
1569  } else if (preg_match('~^(off|no|false)$~', $token)) {
1570  $token = 'false';
1571  } else if ($token == 'null') {
1572  $token = 'null';
1573  } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
1574  /* treat integer literally */
1575  } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {
1576  /* treat as a string, double-quote it escaping quotes */
1577  $token = '"'.addslashes($token).'"';
1578  }
1579 
1580  $attrs[$attr_name] = $token;
1581  $state = 0;
1582  } else
1583  $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1584  break;
1585  }
1586  $last_token = $token;
1587  }
1588 
1589  if($state != 0) {
1590  if($state == 1) {
1591  $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1592  } else {
1593  $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
1594  }
1595  }
1596 
1597  $this->_parse_vars_props($attrs);
1598 
1599  return $attrs;
1600  }
1601 
1608  function _parse_vars_props(&$tokens)
1609  {
1610  foreach($tokens as $key => $val) {
1611  $tokens[$key] = $this->_parse_var_props($val);
1612  }
1613  }
1614 
1623  function _parse_var_props($val)
1624  {
1625  $val = trim($val);
1626 
1627  if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {
1628  // $ variable or object
1629  $return = $this->_parse_var($match[1]);
1630  $modifiers = $match[2];
1631  if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
1632  $_default_mod_string = implode('|',(array)$this->default_modifiers);
1633  $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
1634  }
1635  $this->_parse_modifiers($return, $modifiers);
1636  return $return;
1637  } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1638  // double quoted text
1639  preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1640  $return = $this->_expand_quoted_text($match[1]);
1641  if($match[2] != '') {
1642  $this->_parse_modifiers($return, $match[2]);
1643  }
1644  return $return;
1645  }
1646  elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1647  // numerical constant
1648  preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1649  if($match[2] != '') {
1650  $this->_parse_modifiers($match[1], $match[2]);
1651  return $match[1];
1652  }
1653  }
1654  elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1655  // single quoted text
1656  preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1657  if($match[2] != '') {
1658  $this->_parse_modifiers($match[1], $match[2]);
1659  return $match[1];
1660  }
1661  }
1662  elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1663  // config var
1664  return $this->_parse_conf_var($val);
1665  }
1666  elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1667  // section var
1668  return $this->_parse_section_prop($val);
1669  }
1670  elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
1671  // literal string
1672  return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"');
1673  }
1674  return $val;
1675  }
1676 
1683  function _expand_quoted_text($var_expr)
1684  {
1685  // if contains unescaped $, expand it
1686  if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) {
1687  $_match = $_match[0];
1688  $_replace = array();
1689  foreach($_match as $_var) {
1690  $_replace[$_var] = '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."';
1691  }
1692  $var_expr = strtr($var_expr, $_replace);
1693  $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr);
1694  } else {
1695  $_return = $var_expr;
1696  }
1697  // replace double quoted literal string with single quotes
1698  $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return);
1699  return $_return;
1700  }
1701 
1709  function _parse_var($var_expr)
1710  {
1711  $_has_math = false;
1712  $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
1713 
1714  if(count($_math_vars) > 1) {
1715  $_first_var = "";
1716  $_complete_var = "";
1717  $_output = "";
1718  // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
1719  foreach($_math_vars as $_k => $_math_var) {
1720  $_math_var = $_math_vars[$_k];
1721 
1722  if(!empty($_math_var) || is_numeric($_math_var)) {
1723  // hit a math operator, so process the stuff which came before it
1724  if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
1725  $_has_math = true;
1726  if(!empty($_complete_var) || is_numeric($_complete_var)) {
1727  $_output .= $this->_parse_var($_complete_var);
1728  }
1729 
1730  // just output the math operator to php
1731  $_output .= $_math_var;
1732 
1733  if(empty($_first_var))
1734  $_first_var = $_complete_var;
1735 
1736  $_complete_var = "";
1737  } else {
1738  $_complete_var .= $_math_var;
1739  }
1740  }
1741  }
1742  if($_has_math) {
1743  if(!empty($_complete_var) || is_numeric($_complete_var))
1744  $_output .= $this->_parse_var($_complete_var);
1745 
1746  // get the modifiers working (only the last var from math + modifier is left)
1747  $var_expr = $_complete_var;
1748  }
1749  }
1750 
1751  // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
1752  if(is_numeric(substr($var_expr, 0, 1)))
1753  $_var_ref = $var_expr;
1754  else
1755  $_var_ref = substr($var_expr, 1);
1756 
1757  if(!$_has_math) {
1758 
1759  // get [foo] and .foo and ->foo and (...) pieces
1760  preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match);
1761 
1762  $_indexes = $match[0];
1763  $_var_name = array_shift($_indexes);
1764 
1765  /* Handle $smarty.* variable references as a special case. */
1766  if ($_var_name == 'smarty') {
1767  /*
1768  * If the reference could be compiled, use the compiled output;
1769  * otherwise, fall back on the $smarty variable generated at
1770  * run-time.
1771  */
1772  if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
1773  $_output = $smarty_ref;
1774  } else {
1775  $_var_name = substr(array_shift($_indexes), 1);
1776  $_output = "\$this->_smarty_vars['$_var_name']";
1777  }
1778  } elseif(is_numeric($_var_name) && is_numeric(substr($var_expr, 0, 1))) {
1779  // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
1780  if(count($_indexes) > 0)
1781  {
1782  $_var_name .= implode("", $_indexes);
1783  $_indexes = array();
1784  }
1785  $_output = $_var_name;
1786  } else {
1787  $_output = "\$this->_tpl_vars['$_var_name']";
1788  }
1789 
1790  foreach ($_indexes as $_index) {
1791  if (substr($_index, 0, 1) == '[') {
1792  $_index = substr($_index, 1, -1);
1793  if (is_numeric($_index)) {
1794  $_output .= "[$_index]";
1795  } elseif (substr($_index, 0, 1) == '$') {
1796  if (strpos($_index, '.') !== false) {
1797  $_output .= '[' . $this->_parse_var($_index) . ']';
1798  } else {
1799  $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
1800  }
1801  } else {
1802  $_var_parts = explode('.', $_index);
1803  $_var_section = $_var_parts[0];
1804  $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
1805  $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
1806  }
1807  } else if (substr($_index, 0, 1) == '.') {
1808  if (substr($_index, 1, 1) == '$')
1809  $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
1810  else
1811  $_output .= "['" . substr($_index, 1) . "']";
1812  } else if (substr($_index,0,2) == '->') {
1813  if(substr($_index,2,2) == '__') {
1814  $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1815  } elseif($this->security && substr($_index, 2, 1) == '_') {
1816  $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1817  } elseif (substr($_index, 2, 1) == '$') {
1818  if ($this->security) {
1819  $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1820  } else {
1821  $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
1822  }
1823  } else {
1824  $_output .= $_index;
1825  }
1826  } elseif (substr($_index, 0, 1) == '(') {
1827  $_index = $this->_parse_parenth_args($_index);
1828  $_output .= $_index;
1829  } else {
1830  $_output .= $_index;
1831  }
1832  }
1833  }
1834 
1835  return $_output;
1836  }
1837 
1844  function _parse_parenth_args($parenth_args)
1845  {
1846  preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match);
1847  $orig_vals = $match = $match[0];
1848  $this->_parse_vars_props($match);
1849  $replace = array();
1850  for ($i = 0, $count = count($match); $i < $count; $i++) {
1851  $replace[$orig_vals[$i]] = $match[$i];
1852  }
1853  return strtr($parenth_args, $replace);
1854  }
1855 
1861  function _parse_conf_var($conf_var_expr)
1862  {
1863  $parts = explode('|', $conf_var_expr, 2);
1864  $var_ref = $parts[0];
1865  $modifiers = isset($parts[1]) ? $parts[1] : '';
1866 
1867  $var_name = substr($var_ref, 1, -1);
1868 
1869  $output = "\$this->_config[0]['vars']['$var_name']";
1870 
1871  $this->_parse_modifiers($output, $modifiers);
1872 
1873  return $output;
1874  }
1875 
1882  function _parse_section_prop($section_prop_expr)
1883  {
1884  $parts = explode('|', $section_prop_expr, 2);
1885  $var_ref = $parts[0];
1886  $modifiers = isset($parts[1]) ? $parts[1] : '';
1887 
1888  preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1889  $section_name = $match[1];
1890  $prop_name = $match[2];
1891 
1892  $output = "\$this->_sections['$section_name']['$prop_name']";
1893 
1894  $this->_parse_modifiers($output, $modifiers);
1895 
1896  return $output;
1897  }
1898 
1899 
1907  function _parse_modifiers(&$output, $modifier_string)
1908  {
1909  preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
1910  list(, $_modifiers, $modifier_arg_strings) = $_match;
1911 
1912  for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
1913  $_modifier_name = $_modifiers[$_i];
1914 
1915  if($_modifier_name == 'smarty') {
1916  // skip smarty modifier
1917  continue;
1918  }
1919 
1920  preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
1921  $_modifier_args = $_match[1];
1922 
1923  if (substr($_modifier_name, 0, 1) == '@') {
1924  $_map_array = false;
1925  $_modifier_name = substr($_modifier_name, 1);
1926  } else {
1927  $_map_array = true;
1928  }
1929 
1930  if (empty($this->_plugins['modifier'][$_modifier_name])
1931  && !$this->_get_plugin_filepath('modifier', $_modifier_name)
1932  && function_exists($_modifier_name)) {
1933  if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
1934  $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
1935  } else {
1936  $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false);
1937  }
1938  }
1939  $this->_add_plugin('modifier', $_modifier_name);
1940 
1941  $this->_parse_vars_props($_modifier_args);
1942 
1943  if($_modifier_name == 'default') {
1944  // supress notifications of default modifier vars and args
1945  if(substr($output, 0, 1) == '$') {
1946  $output = '@' . $output;
1947  }
1948  if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') {
1949  $_modifier_args[0] = '@' . $_modifier_args[0];
1950  }
1951  }
1952  if (count($_modifier_args) > 0)
1953  $_modifier_args = ', '.implode(', ', $_modifier_args);
1954  else
1955  $_modifier_args = '';
1956 
1957  if ($_map_array) {
1958  $output = "((is_array(\$_tmp=$output)) ? \$this->_run_mod_handler('$_modifier_name', true, \$_tmp$_modifier_args) : " . $this->_compile_plugin_call('modifier', $_modifier_name) . "(\$_tmp$_modifier_args))";
1959 
1960  } else {
1961 
1962  $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
1963 
1964  }
1965  }
1966  }
1967 
1968 
1976  function _add_plugin($type, $name, $delayed_loading = null)
1977  {
1978  if (!isset($this->_plugin_info[$type])) {
1979  $this->_plugin_info[$type] = array();
1980  }
1981  if (!isset($this->_plugin_info[$type][$name])) {
1982  $this->_plugin_info[$type][$name] = array($this->_current_file,
1983  $this->_current_line_no,
1984  $delayed_loading);
1985  }
1986  }
1987 
1988 
1995  function _compile_smarty_ref(&$indexes)
1996  {
1997  /* Extract the reference name. */
1998  $_ref = substr($indexes[0], 1);
1999  foreach($indexes as $_index_no=>$_index) {
2000  if (substr($_index, 0, 1) != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) {
2001  $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2002  }
2003  }
2004 
2005  switch ($_ref) {
2006  case 'now':
2007  $compiled_ref = 'time()';
2008  $_max_index = 1;
2009  break;
2010 
2011  case 'foreach':
2012  array_shift($indexes);
2013  $_var = $this->_parse_var_props(substr($indexes[0], 1));
2014  $_propname = substr($indexes[1], 1);
2015  $_max_index = 1;
2016  switch ($_propname) {
2017  case 'index':
2018  array_shift($indexes);
2019  $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)";
2020  break;
2021 
2022  case 'first':
2023  array_shift($indexes);
2024  $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)";
2025  break;
2026 
2027  case 'last':
2028  array_shift($indexes);
2029  $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])";
2030  break;
2031 
2032  case 'show':
2033  array_shift($indexes);
2034  $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)";
2035  break;
2036 
2037  default:
2038  unset($_max_index);
2039  $compiled_ref = "\$this->_foreach[$_var]";
2040  }
2041  break;
2042 
2043  case 'section':
2044  array_shift($indexes);
2045  $_var = $this->_parse_var_props(substr($indexes[0], 1));
2046  $compiled_ref = "\$this->_sections[$_var]";
2047  break;
2048 
2049  case 'get':
2050  if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2051  $this->_syntax_error("(secure mode) super global access not permitted",
2052  E_USER_WARNING, __FILE__, __LINE__);
2053  return;
2054  }
2055  $compiled_ref = "\$_GET";
2056  break;
2057 
2058  case 'post':
2059  if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2060  $this->_syntax_error("(secure mode) super global access not permitted",
2061  E_USER_WARNING, __FILE__, __LINE__);
2062  return;
2063  }
2064  $compiled_ref = "\$_POST";
2065  break;
2066 
2067  case 'cookies':
2068  if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2069  $this->_syntax_error("(secure mode) super global access not permitted",
2070  E_USER_WARNING, __FILE__, __LINE__);
2071  return;
2072  }
2073  $compiled_ref = "\$_COOKIE";
2074  break;
2075 
2076  case 'env':
2077  if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2078  $this->_syntax_error("(secure mode) super global access not permitted",
2079  E_USER_WARNING, __FILE__, __LINE__);
2080  return;
2081  }
2082  $compiled_ref = "\$_ENV";
2083  break;
2084 
2085  case 'server':
2086  if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2087  $this->_syntax_error("(secure mode) super global access not permitted",
2088  E_USER_WARNING, __FILE__, __LINE__);
2089  return;
2090  }
2091  $compiled_ref = "\$_SERVER";
2092  break;
2093 
2094  case 'session':
2095  if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2096  $this->_syntax_error("(secure mode) super global access not permitted",
2097  E_USER_WARNING, __FILE__, __LINE__);
2098  return;
2099  }
2100  $compiled_ref = "\$_SESSION";
2101  break;
2102 
2103  /*
2104  * These cases are handled either at run-time or elsewhere in the
2105  * compiler.
2106  */
2107  case 'request':
2108  if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2109  $this->_syntax_error("(secure mode) super global access not permitted",
2110  E_USER_WARNING, __FILE__, __LINE__);
2111  return;
2112  }
2113  if ($this->request_use_auto_globals) {
2114  $compiled_ref = "\$_REQUEST";
2115  break;
2116  } else {
2117  $this->_init_smarty_vars = true;
2118  }
2119  return null;
2120 
2121  case 'capture':
2122  return null;
2123 
2124  case 'template':
2125  $compiled_ref = "'$this->_current_file'";
2126  $_max_index = 1;
2127  break;
2128 
2129  case 'version':
2130  $compiled_ref = "'$this->_version'";
2131  $_max_index = 1;
2132  break;
2133 
2134  case 'const':
2135  if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
2136  $this->_syntax_error("(secure mode) constants not permitted",
2137  E_USER_WARNING, __FILE__, __LINE__);
2138  return;
2139  }
2140  array_shift($indexes);
2141  if (preg_match('!^\.\w+$!', $indexes[0])) {
2142  $compiled_ref = '@' . substr($indexes[0], 1);
2143  } else {
2144  $_val = $this->_parse_var_props(substr($indexes[0], 1));
2145  $compiled_ref = '@constant(' . $_val . ')';
2146  }
2147  $_max_index = 1;
2148  break;
2149 
2150  case 'config':
2151  $compiled_ref = "\$this->_config[0]['vars']";
2152  $_max_index = 3;
2153  break;
2154 
2155  case 'ldelim':
2156  $compiled_ref = "'$this->left_delimiter'";
2157  break;
2158 
2159  case 'rdelim':
2160  $compiled_ref = "'$this->right_delimiter'";
2161  break;
2162 
2163  default:
2164  $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
2165  break;
2166  }
2167 
2168  if (isset($_max_index) && count($indexes) > $_max_index) {
2169  $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2170  }
2171 
2172  array_shift($indexes);
2173  return $compiled_ref;
2174  }
2175 
2186  function _compile_plugin_call($type, $name) {
2187  if (isset($this->_plugins[$type][$name])) {
2188  /* plugin loaded */
2189  if (is_array($this->_plugins[$type][$name][0])) {
2190  return ((is_object($this->_plugins[$type][$name][0][0])) ?
2191  "\$this->_plugins['$type']['$name'][0][0]->" /* method callback */
2192  : (string)($this->_plugins[$type][$name][0][0]).'::' /* class callback */
2193  ). $this->_plugins[$type][$name][0][1];
2194 
2195  } else {
2196  /* function callback */
2197  return $this->_plugins[$type][$name][0];
2198 
2199  }
2200  } else {
2201  /* plugin not loaded -> auto-loadable-plugin */
2202  return 'smarty_'.$type.'_'.$name;
2203 
2204  }
2205  }
2206 
2210  function _load_filters()
2211  {
2212  if (count($this->_plugins['prefilter']) > 0) {
2213  foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
2214  if ($prefilter === false) {
2215  unset($this->_plugins['prefilter'][$filter_name]);
2216  $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
2217  require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2218  smarty_core_load_plugins($_params, $this);
2219  }
2220  }
2221  }
2222  if (count($this->_plugins['postfilter']) > 0) {
2223  foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
2224  if ($postfilter === false) {
2225  unset($this->_plugins['postfilter'][$filter_name]);
2226  $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
2227  require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2228  smarty_core_load_plugins($_params, $this);
2229  }
2230  }
2231  }
2232  }
2233 
2234 
2241  function _quote_replace($string)
2242  {
2243  return strtr($string, array('\\' => '\\\\', '$' => '\\$'));
2244  }
2245 
2254  function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
2255  {
2256  $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
2257  }
2258 
2259 
2266  function _push_cacheable_state($type, $name) {
2267  $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2268  if ($_cacheable
2269  || 0<$this->_cacheable_state++) return '';
2270  if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
2271  $_ret = 'if ($this->caching && !$this->_cache_including): echo \'{nocache:'
2272  . $this->_cache_serial . '#' . $this->_nocache_count
2273  . '}\'; endif;';
2274  return $_ret;
2275  }
2276 
2277 
2284  function _pop_cacheable_state($type, $name) {
2285  $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2286  if ($_cacheable
2287  || --$this->_cacheable_state>0) return '';
2288  return 'if ($this->caching && !$this->_cache_including): echo \'{/nocache:'
2289  . $this->_cache_serial . '#' . ($this->_nocache_count++)
2290  . '}\'; endif;';
2291  }
2292 
2293 
2298  function _push_tag($open_tag)
2299  {
2300  array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
2301  }
2302 
2309  function _pop_tag($close_tag)
2310  {
2311  $message = '';
2312  if (count($this->_tag_stack)>0) {
2313  list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
2314  if ($close_tag == $_open_tag) {
2315  return $_open_tag;
2316  }
2317  if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
2318  return $this->_pop_tag($close_tag);
2319  }
2320  if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
2321  $this->_pop_tag($close_tag);
2322  return $_open_tag;
2323  }
2324  if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
2325  $this->_pop_tag($close_tag);
2326  return $_open_tag;
2327  }
2328  if ($_open_tag == 'else' || $_open_tag == 'elseif') {
2329  $_open_tag = 'if';
2330  } elseif ($_open_tag == 'sectionelse') {
2331  $_open_tag = 'section';
2332  } elseif ($_open_tag == 'foreachelse') {
2333  $_open_tag = 'foreach';
2334  }
2335  $message = " expected {/$_open_tag} (opened line $_line_no).";
2336  }
2337  $this->_syntax_error("mismatched tag {/$close_tag}.$message",
2338  E_USER_ERROR, __FILE__, __LINE__);
2339  }
2340 
2341 }
2342 
2351 function _smarty_sort_length($a, $b)
2352 {
2353  if($a == $b)
2354  return 0;
2355 
2356  if(strlen($a) == strlen($b))
2357  return ($a > $b) ? -1 : 1;
2358 
2359  return (strlen($a) > strlen($b)) ? -1 : 1;
2360 }
2361 
2362 
2363 /* vim: set et: */
2364 
2365 ?>