My favorites | Sign in
Project Logo
                
Changes to /trunk/libs/Smarty_Compiler.class.php
r2796 vs. r2797   Edit
  Compare: vs.   Format:
Revision r2797
Go to: 
Project members, sign in to write a code review
/trunk/libs/Smarty_Compiler.class.php   r2796 /trunk/libs/Smarty_Compiler.class.php   r2797
1 <?php 1 <?php
2 2
3 /** 3 /**
4 * Project: Smarty: the PHP compiling template engine 4 * Project: Smarty: the PHP compiling template engine
5 * File: Smarty_Compiler.class.php 5 * File: Smarty_Compiler.class.php
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * 20 *
21 * @link http://www.smarty.net/ 21 * @link http://www.smarty.net/
22 * @author Monte Ohrt <monte at ohrt dot com> 22 * @author Monte Ohrt <monte at ohrt dot com>
23 * @author Andrei Zmievski <andrei@php.net> 23 * @author Andrei Zmievski <andrei@php.net>
24 * @version 2.6.21-dev 24 * @version 2.6.21-dev
25 * @copyright 2001-2005 New Digital Group, Inc. 25 * @copyright 2001-2005 New Digital Group, Inc.
26 * @package Smarty 26 * @package Smarty
27 */ 27 */
28 28
29 /* $Id$ */ 29 /* $Id$ */
30 30
31 /** 31 /**
32 * Template compiling class 32 * Template compiling class
33 * @package Smarty 33 * @package Smarty
34 */ 34 */
35 class Smarty_Compiler extends Smarty { 35 class Smarty_Compiler extends Smarty {
36 36
37 // internal vars 37 // internal vars
38 /**#@+ 38 /**#@+
39 * @access private 39 * @access private
40 */ 40 */
41 var $_folded_blocks = array(); // keeps folded template blocks 41 var $_folded_blocks = array(); // keeps folded template blocks
42 var $_current_file = null; // the current template being compiled 42 var $_current_file = null; // the current template being compiled
43 var $_current_line_no = 1; // line number for error messages 43 var $_current_line_no = 1; // line number for error messages
44 var $_capture_stack = array(); // keeps track of nested capture buffers 44 var $_capture_stack = array(); // keeps track of nested capture buffers
45 var $_plugin_info = array(); // keeps track of plugins to load 45 var $_plugin_info = array(); // keeps track of plugins to load
46 var $_init_smarty_vars = false; 46 var $_init_smarty_vars = false;
47 var $_permitted_tokens = array('true','false','yes','no','on','off','null'); 47 var $_permitted_tokens = array('true','false','yes','no','on','off','null');
48 var $_db_qstr_regexp = null; // regexps are setup in the constructor 48 var $_db_qstr_regexp = null; // regexps are setup in the constructor
49 var $_si_qstr_regexp = null; 49 var $_si_qstr_regexp = null;
50 var $_qstr_regexp = null; 50 var $_qstr_regexp = null;
51 var $_func_regexp = null; 51 var $_func_regexp = null;
52 var $_reg_obj_regexp = null; 52 var $_reg_obj_regexp = null;
53 var $_var_bracket_regexp = null; 53 var $_var_bracket_regexp = null;
54 var $_num_const_regexp = null; 54 var $_num_const_regexp = null;
55 var $_dvar_guts_regexp = null; 55 var $_dvar_guts_regexp = null;
56 var $_dvar_regexp = null; 56 var $_dvar_regexp = null;
57 var $_cvar_regexp = null; 57 var $_cvar_regexp = null;
58 var $_svar_regexp = null; 58 var $_svar_regexp = null;
59 var $_avar_regexp = null; 59 var $_avar_regexp = null;
60 var $_mod_regexp = null; 60 var $_mod_regexp = null;
61 var $_var_regexp = null; 61 var $_var_regexp = null;
62 var $_parenth_param_regexp = null; 62 var $_parenth_param_regexp = null;
63 var $_func_call_regexp = null; 63 var $_func_call_regexp = null;
64 var $_obj_ext_regexp = null; 64 var $_obj_ext_regexp = null;
65 var $_obj_start_regexp = null; 65 var $_obj_start_regexp = null;
66 var $_obj_params_regexp = null; 66 var $_obj_params_regexp = null;
67 var $_obj_call_regexp = null; 67 var $_obj_call_regexp = null;
68 var $_cacheable_state = 0; 68 var $_cacheable_state = 0;
69 var $_cache_attrs_count = 0; 69 var $_cache_attrs_count = 0;
70 var $_nocache_count = 0; 70 var $_nocache_count = 0;
71 var $_cache_serial = null; 71 var $_cache_serial = null;
72 var $_cache_include = null; 72 var $_cache_include = null;
73 73
74 var $_strip_depth = 0; 74 var $_strip_depth = 0;
75 var $_additional_newline = "\n"; 75 var $_additional_newline = "\n";
76 76
77 var $_phpversion = 0; 77 var $_phpversion = 0;
78 78
79 79
80 /**#@-*/ 80 /**#@-*/
81 /** 81 /**
82 * The class constructor. 82 * The class constructor.
83 */ 83 */
84 function Smarty_Compiler() 84 function Smarty_Compiler()
85 { 85 {
86 $this->_phpversion = substr(phpversion(),0,1); 86 $this->_phpversion = substr(phpversion(),0,1);
87 87
88 // matches double quoted strings: 88 // matches double quoted strings:
89 // "foobar" 89 // "foobar"
90 // "foo\"bar" 90 // "foo\"bar"
91 $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; 91 $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
92 92
93 // matches single quoted strings: 93 // matches single quoted strings:
94 // 'foobar' 94 // 'foobar'
95 // 'foo\'bar' 95 // 'foo\'bar'
96 $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\''; 96 $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
97 97
98 // matches single or double quoted strings 98 // matches single or double quoted strings
99 $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')'; 99 $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
100 100
101 // matches bracket portion of vars 101 // matches bracket portion of vars
102 // [0] 102 // [0]
103 // [foo] 103 // [foo]
104 // [$bar] 104 // [$bar]
105 $this->_var_bracket_regexp = '\[\$?[\w\.]+\]'; 105 $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
106 106
107 // matches numerical constants 107 // matches numerical constants
108 // 30 108 // 30
109 // -12 109 // -12
110 // 13.22 110 // 13.22
111 $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)'; 111 $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
112 112
113 // matches $ vars (not objects): 113 // matches $ vars (not objects):
114 // $foo 114 // $foo
115 // $foo.bar 115 // $foo.bar
116 // $foo.bar.foobar 116 // $foo.bar.foobar
117 // $foo[0] 117 // $foo[0]
118 // $foo[$bar] 118 // $foo[$bar]
119 // $foo[5][blah] 119 // $foo[5][blah]
120 // $foo[5].bar[$foobar][4] 120 // $foo[5].bar[$foobar][4]
121 $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))'; 121 $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))';
122 $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]'; 122 $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
123 $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp 123 $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
124 . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?'; 124 . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
125 $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp; 125 $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
126 126
127 // matches config vars: 127 // matches config vars:
128 // #foo# 128 // #foo#
129 // #foobar123_foo# 129 // #foobar123_foo#
130 $this->_cvar_regexp = '\#\w+\#'; 130 $this->_cvar_regexp = '\#\w+\#';
131 131
132 // matches section vars: 132 // matches section vars:
133 // %foo.bar% 133 // %foo.bar%
134 $this->_svar_regexp = '\%\w+\.\w+\%'; 134 $this->_svar_regexp = '\%\w+\.\w+\%';
135 135
136 // matches all valid variables (no quotes, no modifiers) 136 // matches all valid variables (no quotes, no modifiers)
137 $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|' 137 $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
138 . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')'; 138 . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
139 139
140 // matches valid variable syntax: 140 // matches valid variable syntax:
141 // $foo 141 // $foo
142 // $foo 142 // $foo
143 // #foo# 143 // #foo#
144 // #foo# 144 // #foo#
145 // "text" 145 // "text"
146 // "text" 146 // "text"
147 $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')'; 147 $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
148 148
149 // matches valid object call (one level of object nesting allowed in parameters): 149 // matches valid object call (one level of object nesting allowed in parameters):
150 // $foo->bar 150 // $foo->bar
151 // $foo->bar() 151 // $foo->bar()
152 // $foo->bar("text") 152 // $foo->bar("text")
153 // $foo->bar($foo, $bar, "text") 153 // $foo->bar($foo, $bar, "text")
154 // $foo->bar($foo, "foo") 154 // $foo->bar($foo, "foo")
155 // $foo->bar->foo() 155 // $foo->bar->foo()
156 // $foo->bar->foo->bar() 156 // $foo->bar->foo->bar()
157 // $foo->bar($foo->bar) 157 // $foo->bar($foo->bar)
158 // $foo->bar($foo->bar()) 158 // $foo->bar($foo->bar())
159 // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar)) 159 // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
160 // $foo->getBar()->getFoo() 160 // $foo->getBar()->getFoo()
161 // $foo->getBar()->foo 161 // $foo->getBar()->foo
162 $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')'; 162 $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
163 $this->_obj_restricted_param_regexp = '(?:' 163 $this->_obj_restricted_param_regexp = '(?:'
164 . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')' 164 . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')'
165 . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)'; 165 . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)';
166 166
167 $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|' 167 $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
168 . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)'; 168 . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
169 169
170 $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp 170 $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
171 . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)'; 171 . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
172 $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)'; 172 $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
173 $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . '(?:' . $this->_obj_ext_regexp . '(?:'.$this->_obj_params_regexp . ')?)*' . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?)'; 173 $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . '(?:' . $this->_obj_ext_regexp . '(?:'.$this->_obj_params_regexp . ')?)*' . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?)';
174 174
175 // matches valid modifier syntax: 175 // matches valid modifier syntax:
176 // |foo 176 // |foo
177 // |@foo 177 // |@foo
178 // |foo:"bar" 178 // |foo:"bar"
179 // |foo:$bar 179 // |foo:$bar
180 // |foo:"bar":$foobar 180 // |foo:"bar":$foobar
181 // |foo|bar 181 // |foo|bar
182 // |foo:$foo->bar 182 // |foo:$foo->bar
183 $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|' 183 $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|'
184 . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)'; 184 . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
185 185
186 // matches valid function name: 186 // matches valid function name:
187 // foo123 187 // foo123
188 // _foo_bar 188 // _foo_bar
189 $this->_func_regexp = '[a-zA-Z_]\w*'; 189 $this->_func_regexp = '[a-zA-Z_]\w*';
190 190
191 // matches valid registered object: 191 // matches valid registered object:
192 // foo->bar 192 // foo->bar
193 $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*'; 193 $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
194 194
195 // matches valid parameter values: 195 // matches valid parameter values:
196 // true 196 // true
197 // $foo 197 // $foo
198 // $foo|bar 198 // $foo|bar
199 // #foo# 199 // #foo#
200 // #foo#|bar 200 // #foo#|bar
201 // "text" 201 // "text"
202 // "text"|bar 202 // "text"|bar
203 // $foo->bar 203 // $foo->bar
204 $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|' 204 $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
205 . $this->_var_regexp . '|' . $this->_num_const_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)'; 205 . $this->_var_regexp . '|' . $this->_num_const_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
206 206
207 // matches valid parenthesised function parameters: 207 // matches valid parenthesised function parameters:
208 // 208 //
209 // "text" 209 // "text"
210 // $foo, $bar, "text" 210 // $foo, $bar, "text"
211 // $foo|bar, "foo"|bar, $foo->bar($foo)|bar 211 // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
212 $this->_parenth_param_regexp = '(?:\((?:\w+|' 212 $this->_parenth_param_regexp = '(?:\((?:\w+|'
213 . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|' 213 . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
214 . $this->_param_regexp . ')))*)?\))'; 214 . $this->_param_regexp . ')))*)?\))';
215 215
216 // matches valid function call: 216 // matches valid function call:
217 // foo() 217 // foo()
218 // foo_bar($foo) 218 // foo_bar($foo)
219 // _foo_bar($foo,"bar") 219 // _foo_bar($foo,"bar")
220 // foo123($foo,$foo->bar(),"foo") 220 // foo123($foo,$foo->bar(),"foo")
221 $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:' 221 $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
222 . $this->_parenth_param_regexp . '))'; 222 . $this->_parenth_param_regexp . '))';
223 } 223 }
224 224
225 /** 225 /**
226 * compile a resource 226 * compile a resource
227 * 227 *
228 * sets $compiled_content to the compiled source 228 * sets $compiled_content to the compiled source
229 * @param string $resource_name 229 * @param string $resource_name
230 * @param string $source_content 230 * @param string $source_content
231 * @param string $compiled_content 231 * @param string $compiled_content
232 * @return true 232 * @return true
233 */ 233 */
234 function _compile_file($resource_name, $source_content, &$compiled_content) 234 function _compile_file($resource_name, $source_content, &$compiled_content)
235 { 235 {
236 236
237 if ($this->security) { 237 if ($this->security) {
238 // do not allow php syntax to be executed unless specified 238 // do not allow php syntax to be executed unless specified
239 if ($this->php_handling == SMARTY_PHP_ALLOW && 239 if ($this->php_handling == SMARTY_PHP_ALLOW &&
240 !$this->security_settings['PHP_HANDLING']) { 240 !$this->security_settings['PHP_HANDLING']) {
241 $this->php_handling = SMARTY_PHP_PASSTHRU; 241 $this->php_handling = SMARTY_PHP_PASSTHRU;
242 } 242 }
243 } 243 }
244 244
245 $this->_load_filters(); 245 $this->_load_filters();
246 246
247 $this->_current_file = $resource_name; 247 $this->_current_file = $resource_name;
248 $this->_current_line_no = 1; 248 $this->_current_line_no = 1;
249 $ldq = preg_quote($this->left_delimiter, '~'); 249 $ldq = preg_quote($this->left_delimiter, '~');
250 $rdq = preg_quote($this->right_delimiter, '~'); 250 $rdq = preg_quote($this->right_delimiter, '~');
251 251
252 // run template source through prefilter functions 252 // run template source through prefilter functions
253 if (count($this->_plugins['prefilter']) > 0) { 253 if (count($this->_plugins['prefilter']) > 0) {
254 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) { 254 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
255 if ($prefilter === false) continue; 255 if ($prefilter === false) continue;
256 if ($prefilter[3] || is_callable($prefilter[0])) { 256 if ($prefilter[3] || is_callable($prefilter[0])) {
257 $source_content = call_user_func_array($prefilter[0], 257 $source_content = call_user_func_array($prefilter[0],
258 array($source_content, &$this)); 258 array($source_content, &$this));
259 $this->_plugins['prefilter'][$filter_name][3] = true; 259 $this->_plugins['prefilter'][$filter_name][3] = true;
260 } else { 260 } else {
261 $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented"); 261 $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
262 } 262 }
263 } 263 }
264 } 264 }
265 265
266 /* fetch all special blocks */ 266 /* fetch all special blocks */
267 $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s"; 267 $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
268 268
269 preg_match_all($search, $source_content, $match, PREG_SET_ORDER); 269 preg_match_all($search, $source_content, $match, PREG_SET_ORDER);
270 $this->_folded_blocks = $match; 270 $this->_folded_blocks = $match;
271 reset($this->_folded_blocks); 271 reset($this->_folded_blocks);
272 272
273 /* replace special blocks by "{php}" */ 273 /* replace special blocks by "{php}" */
274 $source_content = preg_replace($search.'e', "'" 274 $source_content = preg_replace($search.'e', "'"
275 . $this->_quote_replace($this->left_delimiter) . 'php' 275 . $this->_quote_replace($this->left_delimiter) . 'php'
276 . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'" 276 . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
277 . $this->_quote_replace($this->right_delimiter) 277 . $this->_quote_replace($this->right_delimiter)
278 . "'" 278 . "'"
279 , $source_content); 279 , $source_content);
280 280
281 /* Gather all template tags. */ 281 /* Gather all template tags. */
282 preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match); 282 preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match);
283 $template_tags = $_match[1]; 283 $template_tags = $_match[1];
284 /* Split content by template tags to obtain non-template content. */ 284 /* Split content by template tags to obtain non-template content. */
285 $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content); 285 $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);
286 286
287 /* loop through text blocks */ 287 /* loop through text blocks */
288 for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) { 288 for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
289 /* match anything resembling php tags */ 289 /* match anything resembling php tags */
290 if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?\s*php\s*[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) { 290 if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?\s*php\s*[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) {
291 /* replace tags with placeholders to prevent recursive replacements */ 291 /* replace tags with placeholders to prevent recursive replacements */
292 $sp_match[1] = array_unique($sp_match[1]); 292 $sp_match[1] = array_unique($sp_match[1]);
293 usort($sp_match[1], '_smarty_sort_length'); 293 usort($sp_match[1], '_smarty_sort_length');
294 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) { 294 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
295 $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]); 295 $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
296 } 296 }
297 /* process each one */ 297 /* process each one */
298 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) { 298 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
299 if ($this->php_handling == SMARTY_PHP_PASSTHRU) { 299 if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
300 /* echo php contents */ 300 /* echo php contents */
301 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]); 301 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
302 } else if ($this->php_handling == SMARTY_PHP_QUOTE) { 302 } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
303 /* quote php tags */ 303 /* quote php tags */
304 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]); 304 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
305 } else if ($this->php_handling == SMARTY_PHP_REMOVE) { 305 } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
306 /* remove php tags */ 306 /* remove php tags */
307 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]); 307 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
308 } else { 308 } else {
309 /* SMARTY_PHP_ALLOW, but echo non php starting tags */ 309 /* SMARTY_PHP_ALLOW, but echo non php starting tags */
310 $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]); 310 $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
311 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]); 311 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
312 } 312 }
313 } 313 }
314 } 314 }
315 } 315 }
316 316
317 /* Compile the template tags into PHP code. */ 317 /* Compile the template tags into PHP code. */
318 $compiled_tags = array(); 318 $compiled_tags = array();
319 for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) { 319 for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
320 $this->_current_line_no += substr_count($text_blocks[$i], "\n"); 320 $this->_current_line_no += substr_count($text_blocks[$i], "\n");
321 $compiled_tags[] = $this->_compile_tag($template_tags[$i]); 321 $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
322 $this->_current_line_no += substr_count($template_tags[$i], "\n"); 322 $this->_current_line_no += substr_count($template_tags[$i], "\n");
323 } 323 }
324 if (count($this->_tag_stack)>0) { 324 if (count($this->_tag_stack)>0) {
325 list($_open_tag, $_line_no) = end($this->_tag_stack); 325 list($_open_tag, $_line_no) = end($this->_tag_stack);
326 $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__); 326 $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
327 return; 327 return;
328 } 328 }
329 329
330 /* Reformat $text_blocks between 'strip' and '/strip' tags, 330 /* Reformat $text_blocks between 'strip' and '/strip' tags,
331 removing spaces, tabs and newlines. */ 331 removing spaces, tabs and newlines. */
332 $strip = false; 332 $strip = false;
333 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) { 333 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
334 if ($compiled_tags[$i] == '{strip}') { 334 if ($compiled_tags[$i] == '{strip}') {
335 $compiled_tags[$i] = ''; 335 $compiled_tags[$i] = '';
336 $strip = true; 336 $strip = true;
337 /* remove leading whitespaces */ 337 /* remove leading whitespaces */
338 $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]); 338 $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
339 } 339 }
340 if ($strip) { 340 if ($strip) {
341 /* strip all $text_blocks before the next '/strip' */ 341 /* strip all $text_blocks before the next '/strip' */
342 for ($j = $i + 1; $j < $for_max; $j++) { 342 for ($j = $i + 1; $j < $for_max; $j++) {
343 /* remove leading and trailing whitespaces of each line */ 343 /* remove leading and trailing whitespaces of each line */
344 $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]); 344 $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]);
345 if ($compiled_tags[$j] == '{/strip}') { 345 if ($compiled_tags[$j] == '{/strip}') {
346 /* remove trailing whitespaces from the last text_block */ 346 /* remove trailing whitespaces from the last text_block */
347 $text_blocks[$j] = rtrim($text_blocks[$j]); 347 $text_blocks[$j] = rtrim($text_blocks[$j]);
348 } 348 }
349 $text_blocks[$j] = "<?php echo '" . strtr($text_blocks[$j], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>"; 349 $text_blocks[$j] = "<?php echo '" . strtr($text_blocks[$j], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>";
350 if ($compiled_tags[$j] == '{/strip}') { 350 if ($compiled_tags[$j] == '{/strip}') {
351 $compiled_tags[$j] = "\n"; /* slurped by php, but necessary 351 $compiled_tags[$j] = "\n"; /* slurped by php, but necessary
352 if a newline is following the closing strip-tag */ 352 if a newline is following the closing strip-tag */
353 $strip = false; 353 $strip = false;
354 $i = $j; 354 $i = $j;
355 break; 355 break;
356 } 356 }
357 } 357 }
358 } 358 }
359 } 359 }
360 $compiled_content = ''; 360 $compiled_content = '';
361 361
362 $tag_guard = '%%%SMARTYOTG' . md5(uniqid(rand(), true)) . '%%%'; 362 $tag_guard = '%%%SMARTYOTG' . md5(uniqid(rand(), true)) . '%%%';
363 363
364 /* Interleave the compiled contents and text blocks to get the final result. */ 364 /* Interleave the compiled contents and text blocks to get the final result. */
365 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) { 365 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
366 if ($compiled_tags[$i] == '') { 366 if ($compiled_tags[$i] == '') {
367 // tag result empty, remove first newline from following text block 367 // tag result empty, remove first newline from following text block
368 $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]); 368 $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]);
369 } 369 }
370 // replace legit PHP tags with placeholder 370 // replace legit PHP tags with placeholder
371 $text_blocks[$i] = str_replace('<?', $tag_guard, $text_blocks[$i]); 371 $text_blocks[$i] = str_replace('<?', $tag_guard, $text_blocks[$i]);
372 $compiled_tags[$i] = str_replace('<?', $tag_guard, $compiled_tags[$i]); 372 $compiled_tags[$i] = str_replace('<?', $tag_guard, $compiled_tags[$i]);
373 373
374 $compiled_content .= $text_blocks[$i] . $compiled_tags[$i]; 374 $compiled_content .= $text_blocks[$i] . $compiled_tags[$i];
375 } 375 }
376 $compiled_content .= str_replace('<?', $tag_guard, $text_blocks[$i]); 376 $compiled_content .= str_replace('<?', $tag_guard, $text_blocks[$i]);
377 377
378 // escape php tags created by interleaving 378 // escape php tags created by interleaving
379 $compiled_content = str_replace('<?', "<?php echo '<?' ?>\n", $compiled_content); 379 $compiled_content = str_replace('<?', "<?php echo '<?' ?>\n", $compiled_content);
380 $compiled_content = preg_replace("~(?<!')language\s*=\s*[\"\']?\s*php\s*[\"\']?~", "<?php echo 'language=php' ?>\n", $compiled_content); 380 $compiled_content = preg_replace("~(?<!')language\s*=\s*[\"\']?\s*php\s*[\"\']?~", "<?php echo 'language=php' ?>\n", $compiled_content);
381 381
382 // recover legit tags 382 // recover legit tags
383 $compiled_content = str_replace($tag_guard, '<?', $compiled_content); 383 $compiled_content = str_replace($tag_guard, '<?', $compiled_content);
384 384
385 // remove \n from the end of the file, if any 385 // remove \n from the end of the file, if any
386 if (strlen($compiled_content) && (substr($compiled_content, -1) == "\n") ) { 386 if (strlen($compiled_content) && (substr($compiled_content, -1) == "\n") ) {
387 $compiled_content = substr($compiled_content, 0, -1); 387 $compiled_content = substr($compiled_content, 0, -1);
388 } 388 }
389 389
390 if (!empty($this->_cache_serial)) { 390 if (!empty($this->_cache_serial)) {
391 $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content; 391 $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
392 } 392 }
393 393
394 // run compiled template through postfilter functions 394 // run compiled template through postfilter functions
395 if (count($this->_plugins['postfilter']) > 0) { 395 if (count($this->_plugins['postfilter']) > 0) {
396 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) { 396 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
397 if ($postfilter === false) continue; 397 if ($postfilter === false) continue;
398 if ($postfilter[3] || is_callable($postfilter[0])) { 398 if ($postfilter[3] || is_callable($postfilter[0])) {
399 $compiled_content = call_user_func_array($postfilter[0], 399 $compiled_content = call_user_func_array($postfilter[0],
400 array($compiled_content, &$this)); 400 array($compiled_content, &$this));
401 $this->_plugins['postfilter'][$filter_name][3] = true; 401 $this->_plugins['postfilter'][$filter_name][3] = true;
402 } else { 402 } else {
403 $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented"); 403 $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
404 } 404 }
405 } 405 }
406 } 406 }
407 407
408 // put header at the top of the compiled template 408 // put header at the top of the compiled template
409 $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n"; 409 $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
410 $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n"; 410 $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
411 411
412 /* Emit code to load needed plugins. */ 412 /* Emit code to load needed plugins. */
413 $this->_plugins_code = ''; 413 $this->_plugins_code = '';
414 if (count($this->_plugin_info)) { 414 if (count($this->_plugin_info)) {
415 $_plugins_params = "array('plugins' => array("; 415 $_plugins_params = "array('plugins' => array(";
416 foreach ($this->_plugin_info as $plugin_type => $plugins) { 416 foreach ($this->_plugin_info as $plugin_type => $plugins) {
417 foreach ($plugins as $plugin_name => $plugin_info) { 417 foreach ($plugins as $plugin_name => $plugin_info) {
418 $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], "; 418 $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
419 $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),'; 419 $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
420 } 420 }
421 } 421 }
422 $_plugins_params .= '))'; 422 $_plugins_params .= '))';
423 $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n"; 423 $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
424 $template_header .= $plugins_code; 424 $template_header .= $plugins_code;
425 $this->_plugin_info = array(); 425 $this->_plugin_info = array();
426 $this->_plugins_code = $plugins_code; 426 $this->_plugins_code = $plugins_code;
427 } 427 }
428 428
429 if ($this->_init_smarty_vars) { 429 if ($this->_init_smarty_vars) {
430 $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n"; 430 $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
431 $this->_init_smarty_vars = false; 431 $this->_init_smarty_vars = false;
432 } 432 }
433 433
434 $compiled_content = $template_header . $compiled_content; 434 $compiled_content = $template_header . $compiled_content;
435 return true; 435 return true;
436 } 436 }
437 437
438 /** 438 /**
439 * Compile a template tag 439 * Compile a template tag
440 * 440 *
441 * @param string $template_tag 441 * @param string $template_tag
442 * @return string 442 * @return string
443 */ 443 */
444 function _compile_tag($template_tag) 444 function _compile_tag($template_tag)
445 { 445 {
446 /* Matched comment. */ 446 /* Matched comment. */
447 if (substr($template_tag, 0, 1) == '*' && substr($template_tag, -1) == '*') 447 if (substr($template_tag, 0, 1) == '*' && substr($template_tag, -1) == '*')
448 return ''; 448 return '';
449 449
450 /* Split tag into two three parts: command, command modifiers and the arguments. */ 450 /* Split tag into two three parts: command, command modifiers and the arguments. */
451 if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp 451 if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp
452 . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*)) 452 . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
453 (?:\s+(.*))?$ 453 (?:\s+(.*))?$
454 ~xs', $template_tag, $match)) { 454 ~xs', $template_tag, $match)) {
455 $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__); 455 $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
456 } 456 }
457 457
458 $tag_command = $match[1]; 458 $tag_command = $match[1];
459 $tag_modifier = isset($match[2]) ? $match[2] : null; 459 $tag_modifier = isset($match[2]) ? $match[2] : null;
460 $tag_args = isset($match[3]) ? $match[3] : null; 460 $tag_args = isset($match[3]) ? $match[3] : null;
461 461
462 if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) { 462 if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {
463 /* tag name is a variable or object */ 463 /* tag name is a variable or object */
464 $_return = $this->_parse_var_props($tag_command . $tag_modifier); 464 $_return = $this->_parse_var_props($tag_command . $tag_modifier);
465 return "<?php echo $_return; ?>" . $this->_additional_newline; 465 return "<?php echo $_return; ?>" . $this->_additional_newline;
466 } 466 }
467 467
468 /* If the tag name is a registered object, we process it. */ 468 /* If the tag name is a registered object, we process it. */
469 if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) { 469 if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) {
470 return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier); 470 return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
471 } 471 }
472 472
473 switch ($tag_command) { 473 switch ($tag_command) {
474 case 'include': 474 case 'include':
475 return $this->_compile_include_tag($tag_args); 475 return $this->_compile_include_tag($tag_args);
476 476
477 case 'include_php': 477 case 'include_php':
478 return $this->_compile_include_php_tag($tag_args); 478 return $this->_compile_include_php_tag($tag_args);
479 479
480 case 'if': 480 case 'if':
481 $this->_push_tag('if'); 481 $this->_push_tag('if');
482 return $this->_compile_if_tag($tag_args); 482 return $this->_compile_if_tag($tag_args);
483 483
484 case 'else': 484 case 'else':
485 list($_open_tag) = end($this->_tag_stack); 485 list($_open_tag) = end($this->_tag_stack);
486 if ($_open_tag != 'if' && $_open_tag != 'elseif') 486 if ($_open_tag != 'if' && $_open_tag != 'elseif')
487 $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__); 487 $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
488 else 488 else
489 $this->_push_tag('else'); 489 $this->_push_tag('else');
490 return '<?php else: ?>'; 490 return '<?php else: ?>';
491 491
492 case 'elseif': 492 case 'elseif':
493 list($_open_tag) = end($this->_tag_stack); 493 list($_open_tag) = end($this->_tag_stack);
494 if ($_open_tag != 'if' && $_open_tag != 'elseif') 494 if ($_open_tag != 'if' && $_open_tag != 'elseif')
495 $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__); 495 $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
496 if ($_open_tag == 'if') 496 if ($_open_tag == 'if')
497 $this->_push_tag('elseif'); 497 $this->_push_tag('elseif');
498 return $this->_compile_if_tag($tag_args, true); 498 return $this->_compile_if_tag($tag_args, true);
499 499
500 case '/if': 500 case '/if':
501 $this->_pop_tag('if'); 501 $this->_pop_tag('if');
502 return '<?php endif; ?>'; 502 return '<?php endif; ?>';
503 503
504 case 'capture': 504 case 'capture':
505 return $this->_compile_capture_tag(true, $tag_args); 505 return $this->_compile_capture_tag(true, $tag_args);
506 506
507 case '/capture': 507 case '/capture':
508 return $this->_compile_capture_tag(false); 508 return $this->_compile_capture_tag(false);
509 509
510 case 'ldelim': 510 case 'ldelim':
511 return $this->left_delimiter; 511 return $this->left_delimiter;
512 512
513 case 'rdelim': 513 case 'rdelim':
514 return $this->right_delimiter; 514 return $this->right_delimiter;
515 515
516 case 'section': 516 case 'section':
517 $this->_push_tag('section'); 517 $this->_push_tag('section');
518 return $this->_compile_section_start($tag_args); 518 return $this->_compile_section_start($tag_args);
519 519
520 case 'sectionelse': 520 case 'sectionelse':
521 $this->_push_tag('sectionelse'); 521 $this->_push_tag('sectionelse');
522 return "<?php endfor; else: ?>"; 522 return "<?php endfor; else: ?>";
523 break; 523 break;
524 524
525 case '/section': 525 case '/section':
526 $_open_tag = $this->_pop_tag('section'); 526 $_open_tag = $this->_pop_tag('section');
527 if ($_open_tag == 'sectionelse') 527 if ($_open_tag == 'sectionelse')
528 return "<?php endif; ?>"; 528 return "<?php endif; ?>";
529 else 529 else
530 return "<?php endfor; endif; ?>"; 530 return "<?php endfor; endif; ?>";
531 531
532 case 'foreach': 532 case 'foreach':
533 $this->_push_tag('foreach'); 533 $this->_push_tag('foreach');
534 return $this->_compile_foreach_start($tag_args); 534 return $this->_compile_foreach_start($tag_args);
535 break; 535 break;
536 536
537 case 'foreachelse': 537 case 'foreachelse':
538 $this->_push_tag('foreachelse'); 538 $this->_push_tag('foreachelse');
539 return "<?php endforeach; else: ?>"; 539 return "<?php endforeach; else: ?>";
540 540
541 case '/foreach': 541 case '/foreach':
542 $_open_tag = $this->_pop_tag('foreach'); 542 $_open_tag = $this->_pop_tag('foreach');
543 if ($_open_tag == 'foreachelse') 543 if ($_open_tag == 'foreachelse')
544 return "<?php endif; unset(\$_from); ?>"; 544 return "<?php endif; unset(\$_from); ?>";
545 else 545 else
546 return "<?php endforeach; endif; unset(\$_from); ?>"; 546 return "<?php endforeach; endif; unset(\$_from); ?>";
547 break; 547 break;
548 548
549 case 'strip': 549 case 'strip':
550 case '/strip': 550 case '/strip':
551 if (substr($tag_command, 0, 1)=='/') { 551 if (substr($tag_command, 0, 1)=='/') {
552 $this->_pop_tag('strip'); 552 $this->_pop_tag('strip');
553 if (--$this->_strip_depth==0) { /* outermost closing {/strip} */ 553 if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
554 $this->_additional_newline = "\n"; 554 $this->_additional_newline = "\n";
555 return '{' . $tag_command . '}'; 555 return '{' . $tag_command . '}';
556 } 556 }
557 } else { 557 } else {
558 $this->_push_tag('strip'); 558 $this->_push_tag('strip');
559 if ($this->_strip_depth++==0) { /* outermost opening {strip} */ 559 if ($this->_strip_depth++==0) { /* outermost opening {strip} */
560 $this->_additional_newline = ""; 560 $this->_additional_newline = "";
561 return '{' . $tag_command . '}'; 561 return '{' . $tag_command . '}';
562 } 562 }
563 } 563 }
564 return ''; 564 return '';
565 565
566 case 'php': 566 case 'php':
567 /* handle folded tags replaced by {php} */ 567 /* handle folded tags replaced by {php} */
568 list(, $block) = each($this->_folded_blocks); 568 list(, $block) = each($this->_folded_blocks);
569 $this->_current_line_no += substr_count($block[0], "\n"); 569 $this->_current_line_no += substr_count($block[0], "\n");
570 /* the number of matched elements in the regexp in _compile_file() 570 /* the number of matched elements in the regexp in _compile_file()
571 determins the type of folded tag that was found */ 571 determins the type of folded tag that was found */
572 switch (count($block)) { 572 switch (count($block)) {
573 case 2: /* comment */ 573 case 2: /* comment */
574 return ''; 574 return '';
575 575
576 case 3: /* literal */ 576 case 3: /* literal */
577 return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline; 577 return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
578 578
579 case 4: /* php */ 579 case 4: /* php */
580 if ($this->security && !$this->security_settings['PHP_TAGS']) { 580 if ($this->security && !$this->security_settings['PHP_TAGS']) {
581 $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__); 581 $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
582 return; 582 return;
583 } 583 }
584 return '<?php ' . $block[3] .' ?>'; 584 return '<?php ' . $block[3] .' ?>';
585 } 585 }
586 break; 586 break;
587 587
588 case 'insert': 588 case 'insert':
589 return $this->_compile_insert_tag($tag_args); 589 return $this->_compile_insert_tag($tag_args);
590 590
591 default: 591 default:
592 if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) { 592 if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
593 return $output; 593 return $output;
594 } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) { 594 } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
595 return $output; 595 return $output;
596 } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) { 596 } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
597 return $output; 597 return $output;
598 } else { 598 } else {
599 $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__); 599 $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
600 } 600 }
601 601
602 } 602 }
603 } 603 }
604 604
605 605
606 /** 606 /**
607 * compile the custom compiler tag 607 * compile the custom compiler tag
608 * 608 *
609 * sets $output to the compiled custom compiler tag 609 * sets $output to the compiled custom compiler tag
610 * @param string $tag_command 610 * @param string $tag_command
611 * @param string $tag_args 611 * @param string $tag_args
612 * @param string $output 612 * @param string $output
613 * @return boolean 613 * @return boolean
614 */ 614 */
615 function _compile_compiler_tag($tag_command, $tag_args, &$output) 615 function _compile_compiler_tag($tag_command, $tag_args, &$output)
616 { 616 {
617 $found = false; 617 $found = false;
618 $have_function = true; 618 $have_function = true;
619 619
620 /* 620 /*
621 * First we check if the compiler function has already been registered 621 * First we check if the compiler function has already been registered
622 * or loaded from a plugin file. 622 * or loaded from a plugin file.
623 */ 623 */
624 if (isset($this->_plugins['compiler'][$tag_command])) { 624 if (isset($this->_plugins['compiler'][$tag_command])) {
625 $found = true; 625 $found = true;
626 $plugin_func = $this->_plugins['compiler'][$tag_command][0]; 626 $plugin_func = $this->_plugins['compiler'][$tag_command][0];
627 if (!is_callable($plugin_func)) { 627 if (!is_callable($plugin_func)) {
628 $message = "compiler function '$tag_command' is not implemented"; 628 $message = "compiler function '$tag_command' is not implemented";
629 $have_function = false; 629 $have_function = false;
630 } 630 }
631 } 631 }
632 /* 632 /*
633 * Otherwise we need to load plugin file and look for the function 633 * Otherwise we need to load plugin file and look for the function
634 * inside it. 634 * inside it.
635 */ 635 */
636 else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) { 636 else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
637 $found = true; 637 $found = true;
638 638
639 include_once $plugin_file; 639 include_once $plugin_file;
640 640
641 $plugin_func = 'smarty_compiler_' . $tag_command; 641 $plugin_func = 'smarty_compiler_' . $tag_command;
642 if (!is_callable($plugin_func)) { 642 if (!is_callable($plugin_func)) {
643 $message = "plugin function $plugin_func() not found in $plugin_file\n"; 643 $message = "plugin function $plugin_func() not found in $plugin_file\n";
644 $have_function = false; 644 $have_function = false;
645 } else { 645 } else {
646 $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true); 646 $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
647 } 647 }
648 } 648 }
649 649
650 /* 650 /*
651 * True return value means that we either found a plugin or a 651 * True return value means that we either found a plugin or a
652 * dynamically registered function. False means that we didn't and the 652 * dynamically registered function. False means that we didn't and the
653 * compiler should now emit code to load custom function plugin for this 653 * compiler should now emit code to load custom function plugin for this
654 * tag. 654 * tag.
655 */ 655 */
656 if ($found) { 656 if ($found) {
657 if ($have_function) { 657 if ($have_function) {
658 $output = call_user_func_array($plugin_func, array($tag_args, &$this)); 658 $output = call_user_func_array($plugin_func, array($tag_args, &$this));
659 if($output != '') { 659 if($output != '') {
660 $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command) 660 $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
661 . $output 661 . $output
662 . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>'; 662 . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
663 } 663 }
664 } else { 664 } else {
665 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__); 665 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
666 } 666 }
667 return true; 667 return true;
668 } else { 668 } else {
669 return false; 669 return false;
670 } 670 }
671 } 671 }
672 672
673 673
674 /** 674 /**
675 * compile block function tag 675 * compile block function tag
676 * 676 *
677 * sets $output to compiled block function tag 677 * sets $output to compiled block function tag
678 * @param string $tag_command 678 * @param string $tag_command
679 * @param string $tag_args 679 * @param string $tag_args
680 * @param string $tag_modifier 680 * @param string $tag_modifier
681 * @param string $output 681 * @param string $output
682 * @return boolean 682 * @return boolean
683 */ 683 */
684 function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output) 684 function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
685 { 685 {
686 if (substr($tag_command, 0, 1) == '/') { 686 if (substr($tag_command, 0, 1) == '/') {
687 $start_tag = false; 687 $start_tag = false;
688 $tag_command = substr($tag_command, 1); 688 $tag_command = substr($tag_command, 1);
689 } else 689 } else
690 $start_tag = true; 690 $start_tag = true;
691 691
692 $found = false; 692 $found = false;
693 $have_function = true; 693 $have_function = true;
694 694
695 /* 695 /*
696 * First we check if the block function has already been registered 696 * First we check if the block function has already been registered
697 * or loaded from a plugin file. 697 * or loaded from a plugin file.
698 */ 698 */
699 if (isset($this->_plugins['block'][$tag_command])) { 699 if (isset($this->_plugins['block'][$tag_command])) {
700 $found = true; 700 $found = true;
701 $plugin_func = $this->_plugins['block'][$tag_command][0]; 701 $plugin_func = $this->_plugins['block'][$tag_command][0];
702 if (!is_callable($plugin_func)) { 702 if (!is_callable($plugin_func)) {
703 $message = "block function '$tag_command' is not implemented"; 703 $message = "block function '$tag_command' is not implemented";
704 $have_function = false; 704 $have_function = false;
705 } 705 }
706 } 706 }
707 /* 707 /*
708 * Otherwise we need to load plugin file and look for the function 708 * Otherwise we need to load plugin file and look for the function
709 * inside it. 709 * inside it.
710 */ 710 */
711 else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) { 711 else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
712 $found = true; 712 $found = true;
713 713
714 include_once $plugin_file; 714 include_once $plugin_file;
715 715
716 $plugin_func = 'smarty_block_' . $tag_command; 716 $plugin_func = 'smarty_block_' . $tag_command;
717 if (!function_exists($plugin_func)) { 717 if (!function_exists($plugin_func)) {
718 $message = "plugin function $plugin_func() not found in $plugin_file\n"; 718 $message = "plugin function $plugin_func() not found in $plugin_file\n";
719 $have_function = false; 719 $have_function = false;
720 } else { 720 } else {
721 $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true); 721 $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
722 722
723 } 723 }
724 } 724 }
725 725
726 if (!$found) { 726 if (!$found) {
727 return false; 727 return false;
728 } else if (!$have_function) { 728 } else if (!$have_function) {
729 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__); 729 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
730 return true; 730 return true;
731 } 731 }
732 732
733 /* 733 /*
734 * Even though we've located the plugin function, compilation 734 * Even though we've located the plugin function, compilation
735 * happens only once, so the plugin will still need to be loaded 735 * happens only once, so the plugin will still need to be loaded
736 * at runtime for future requests. 736 * at runtime for future requests.
737 */ 737 */
738 $this->_add_plugin('block', $tag_command); 738 $this->_add_plugin('block', $tag_command);
739 739
740 if ($start_tag) 740 if ($start_tag)
741 $this->_push_tag($tag_command); 741 $this->_push_tag($tag_command);
742 else 742 else
743 $this->_pop_tag($tag_command); 743 $this->_pop_tag($tag_command);
744 744
745 if ($start_tag) { 745 if ($start_tag) {
746 $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command); 746 $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
747 $attrs = $this->_parse_attrs($tag_args); 747 $attrs = $this->_parse_attrs($tag_args);
748 $_cache_attrs=''; 748 $_cache_attrs='';
749 $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs); 749 $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs);
750 $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); '; 750 $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
751 $output .= '$_block_repeat=true;' . $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat);'; 751 $output .= '$_block_repeat=true;' . $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat);';
752 $output .= 'while ($_block_repeat) { ob_start(); ?>'; 752 $output .= 'while ($_block_repeat) { ob_start(); ?>';
753 } else { 753 } else {
754 $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); '; 754 $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); ';
755 $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat)'; 755 $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat)';
756 if ($tag_modifier != '') { 756 if ($tag_modifier != '') {
757 $this->_parse_modifiers($_out_tag_text, $tag_modifier); 757 $this->_parse_modifiers($_out_tag_text, $tag_modifier);
758 } 758 }
759 $output .= '$_block_repeat=false;echo ' . $_out_tag_text . '; } '; 759 $output .= '$_block_repeat=false;echo ' . $_out_tag_text . '; } ';
760 $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>'; 760 $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
761 } 761 }
762 762
763 return true; 763 return true;
764 } 764 }
765 765
766 766
767 /** 767 /**
768 * compile custom function tag 768 * compile custom function tag
769 * 769 *
770 * @param string $tag_command 770 * @param string $tag_command
771 * @param string $tag_args 771 * @param string $tag_args
772 * @param string $tag_modifier 772 * @param string $tag_modifier
773 * @return string 773 * @return string
774 */ 774 */
775 function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output) 775 function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
776 { 776 {
777 $found = false; 777 $found = false;
778 $have_function = true; 778 $have_function = true;
779 779
780 /* 780 /*
781 * First we check if the custom function has already been registered 781 * First we check if the custom function has already been registered
782 * or loaded from a plugin file. 782 * or loaded from a plugin file.
783 */ 783 */
784 if (isset($this->_plugins['function'][$tag_command])) { 784 if (isset($this->_plugins['function'][$tag_command])) {
785 $found = true; 785 $found = true;
786 $plugin_func = $this->_plugins['function'][$tag_command][0]; 786 $plugin_func = $this->_plugins['function'][$tag_command][0];
787 if (!is_callable($plugin_func)) { 787 if (!is_callable($plugin_func)) {
788 $message = "custom function '$tag_command' is not implemented"; 788 $message = "custom function '$tag_command' is not implemented";
789 $have_function = false; 789 $have_function = false;
790 } 790 }
791 } 791 }
792 /* 792 /*
793 * Otherwise we need to load plugin file and look for the function 793 * Otherwise we need to load plugin file and look for the function
794 * inside it. 794 * inside it.
795 */ 795 */
796 else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) { 796 else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
797 $found = true; 797 $found = true;
798 798
799 include_once $plugin_file; 799 include_once $plugin_file;
800 800
801 $plugin_func = 'smarty_function_' . $tag_command; 801 $plugin_func = 'smarty_function_' . $tag_command;
802 if (!function_exists($plugin_func)) { 802 if (!function_exists($plugin_func)) {
803 $message = "plugin function $plugin_func() not found in $plugin_file\n"; 803 $message = "plugin function $plugin_func() not found in $plugin_file\n";
804 $have_function = false; 804 $have_function = false;
805 } else { 805 } else {
806 $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true); 806 $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true);
807 807
808 } 808 }
809 } 809 }
810 810
811 if (!$found) { 811 if (!$found) {
812 return false; 812 return false;
813 } else if (!$have_function) { 813 } else if (!$have_function) {
814 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__); 814 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
815 return true; 815 return true;
816 } 816 }
817 817
818 /* declare plugin to be loaded on display of the template that 818 /* declare plugin to be loaded on display of the template that
819 we compile right now */ 819 we compile right now */
820 $this->_add_plugin('function', $tag_command); 820 $this->_add_plugin('function', $tag_command);
821 821
822 $_cacheable_state = $this->_push_cacheable_state('function', $tag_command); 822 $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
823 $attrs = $this->_parse_attrs($tag_args); 823 $attrs = $this->_parse_attrs($tag_args);
824 $_cache_attrs = ''; 824 $_cache_attrs = '';
825 $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs); 825 $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs);
826 826
827 $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)"; 827 $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
828 if($tag_modifier != '') { 828 if($tag_modifier != '') {
829 $this->_parse_modifiers($output, $tag_modifier); 829 $this->_parse_modifiers($output, $tag_modifier);
830 } 830 }
831 831
832 if($output != '') { 832 if($output != '') {
833 $output = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';' 833 $output = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
834 . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline; 834 . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
835 } 835 }
836 836
837 return true; 837 return true;
838 } 838 }
839 839
840 /** 840 /**
841 * compile a registered object tag 841 * compile a registered object tag
842 * 842 *
843 * @param string $tag_command 843 * @param string $tag_command
844 * @param array $attrs 844 * @param array $attrs
845 * @param string $tag_modifier 845 * @param string $tag_modifier
846 * @return string 846 * @return string
847 */ 847 */
848 function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier) 848 function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
849 { 849 {
850 if (substr($tag_command, 0, 1) == '/') { 850 if (substr($tag_command, 0, 1) == '/') {
851 $start_tag = false; 851 $start_tag = false;
852 $tag_command = substr($tag_command, 1); 852 $tag_command = substr($tag_command, 1);
853 } else { 853 } else {
854 $start_tag = true; 854 $start_tag = true;
855 } 855 }
856 856
857 list($object, $obj_comp) = explode('->', $tag_command); 857 list($object, $obj_comp) = explode('->', $tag_command);
858 858
859 $arg_list = array(); 859 $arg_list = array();
860 if(count($attrs)) { 860 if(count($attrs)) {
861 $_assign_var = false; 861 $_assign_var = false;
862 foreach ($attrs as $arg_name => $arg_value) { 862 foreach ($attrs as $arg_name => $arg_value) {
863 if($arg_name == 'assign') { 863 if($arg_name == 'assign') {
864 $_assign_var = $arg_value; 864 $_assign_var = $arg_value;
865 unset($attrs['assign']); 865 unset($attrs['assign']);
866 continue; 866 continue;
867 } 867 }
868 if (is_bool($arg_value)) 868 if (is_bool($arg_value))
869 $arg_value = $arg_value ? 'true' : 'false'; 869 $arg_value = $arg_value ? 'true' : 'false';
870 $arg_list[] = "'$arg_name' => $arg_value"; 870 $arg_list[] = "'$arg_name' => $arg_value";
871 } 871 }
872 } 872 }
873 873
874 if($this->_reg_objects[$object][2]) { 874 if($this->_reg_objects[$object][2]) {
875 // smarty object argument format 875 // smarty object argument format
876 $args = "array(".implode(',', (array)$arg_list)."), \$this"; 876 $args = "array(".implode(',', (array)$arg_list)."), \$this";
877 } else { 877 } else {
878 // traditional argument format 878 // traditional argument format
879 $args = implode(',', array_values($attrs)); 879 $args = implode(',', array_values($attrs));
880 if (empty($args)) { 880 if (empty($args)) {
881 $args = ''; 881 $args = '';
882 } 882 }
883 } 883 }
884 884
885 $prefix = ''; 885 $prefix = '';
886 $postfix = ''; 886 $postfix = '';
887 $newline = ''; 887 $newline = '';
888 if(!is_object($this->_reg_objects[$object][0])) { 888 if(!is_object($this->_reg_objects[$object][0])) {
889 $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__); 889 $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
890 } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) { 890 } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
891 $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__); 891 $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
892 } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) { 892 } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
893 // method 893 // method
894 if(in_array($obj_comp, $this->_reg_objects[$object][3])) { 894 if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
895 // block method 895 // block method
896 if ($start_tag) { 896 if ($start_tag) {
897 $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); "; 897 $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
898 $prefix .= "\$_block_repeat=true; \$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat); "; 898 $prefix .= "\$_block_repeat=true; \$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat); ";
899 $prefix .= "while (\$_block_repeat) { ob_start();"; 899 $prefix .= "while (\$_block_repeat) { ob_start();";
900 $return = null; 900 $return = null;
901 $postfix = ''; 901 $postfix = '';
902 } else { 902 } else {
903 $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); \$_block_repeat=false;"; 903 $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); \$_block_repeat=false;";
904 $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat)"; 904 $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat)";
905 $postfix = "} array_pop(\$this->_tag_stack);"; 905 $postfix = "} array_pop(\$this->_tag_stack);";
906 } 906 }
907 } else { 907 } else {
908 // non-block method 908 // non-block method
909 $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)"; 909 $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
910 } 910 }
911 } else { 911 } else {
912 // property 912 // property
913 $return = "\$this->_reg_objects['$object'][0]->$obj_comp"; 913 $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
914 } 914 }
915 915
916 if($return != null) { 916 if($return != null) {
917 if($tag_modifier != '') { 917 if($tag_modifier != '') {
918 $this->_parse_modifiers($return, $tag_modifier); 918 $this->_parse_modifiers($return, $tag_modifier);
919 } 919 }
920 920
921 if(!empty($_assign_var)) { 921 if(!empty($_assign_var)) {
922 $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);"; 922 $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);";
923 } else { 923 } else {
924 $output = 'echo ' . $return . ';'; 924 $output = 'echo ' . $return . ';';
925 $newline = $this->_additional_newline; 925 $newline = $this->_additional_newline;
926 } 926 }
927 } else { 927 } else {
928 $output = ''; 928 $output = '';
929 } 929 }
930 930
931 return '<?php ' . $prefix . $output . $postfix . "?>" . $newline; 931 return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
932 } 932 }
933 933
934 /** 934 /**
935 * Compile {insert ...} tag 935 * Compile {insert ...} tag
936 * 936 *
937 * @param string $tag_args 937 * @param string $tag_args
938 * @return string 938 * @return string
939 */ 939 */
940 function _compile_insert_tag($tag_args) 940 function _compile_insert_tag($tag_args)
941 { 941 {
942 $attrs = $this->_parse_attrs($tag_args); 942 $attrs = $this->_parse_attrs($tag_args);
943 $name = $this->_dequote($attrs['name']); 943 $name = $this->_dequote($attrs['name']);
944 944
945 if (empty($name)) { 945 if (empty($name)) {
946 return $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__); 946 return $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
947 } 947 }
948 948
949 if (!preg_match('~^\w+$~', $name)) { 949 if (!preg_match('~^\w+$~', $name)) {
950 return $this->_syntax_error("'insert: 'name' must be an insert function name", E_USER_ERROR, __FILE__, __LINE__); 950 return $this->_syntax_error("'insert: 'name' must be an insert function name", E_USER_ERROR, __FILE__, __LINE__);
951 } 951 }
952 952
953 if (!empty($attrs['script'])) { 953 if (!empty($attrs['script'])) {
954 $delayed_loading = true; 954 $delayed_loading = true;
955 } else { 955 } else {
956 $delayed_loading = false; 956 $delayed_loading = false;
957 } 957 }
958 958
959 foreach ($attrs as $arg_name => $arg_value) { 959 foreach ($attrs as $arg_name => $arg_value) {
960 if (is_bool($arg_value)) 960 if (is_bool($arg_value))
961 $arg_value = $arg_value ? 'true' : 'false'; 961 $arg_value = $arg_value ? 'true' : 'false';
962 $arg_list[] = "'$arg_name' => $arg_value"; 962 $arg_list[] = "'$arg_name' => $arg_value";
963 } 963 }
964 964
965 $this->_add_plugin('insert', $name, $delayed_loading); 965 $this->_add_plugin('insert', $name, $delayed_loading);
966 966
967 $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))"; 967 $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
968 968
969 return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline; 969 return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
970 } 970 }
971 971
972 /** 972 /**
973 * Compile {include ...} tag 973 * Compile {include ...} tag
974 * 974 *
975 * @param string $tag_args 975 * @param string $tag_args
976 * @return string 976 * @return string
977 */ 977 */
978 function _compile_include_tag($tag_args) 978 function _compile_include_tag($tag_args)
979 { 979 {
980 $attrs = $this->_parse_attrs($tag_args); 980 $attrs = $this->_parse_attrs($tag_args);
981 $arg_list = array(); 981 $arg_list = array();
982 982
983 if (empty($attrs['file'])) { 983 if (empty($attrs['file'])) {
984 $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__); 984 $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
985 } 985 }
986 986
987 foreach ($attrs as $arg_name => $arg_value) { 987 foreach ($attrs as $arg_name => $arg_value) {
988 if ($arg_name == 'file') { 988 if ($arg_name == 'file') {
989 $include_file = $arg_value; 989 $include_file = $arg_value;
990 continue; 990 continue;
991 } else if ($arg_name == 'assign') { 991 } else if ($arg_name == 'assign') {
992 $assign_var = $arg_value; 992 $assign_var = $arg_value;
993 continue; 993 continue;
994 } 994 }
995 if (is_bool($arg_value)) 995 if (is_bool($arg_value))
996 $arg_value = $arg_value ? 'true' : 'false'; 996 $arg_value = $arg_value ? 'true' : 'false';
997 $arg_list[] = "'$arg_name' => $arg_value"; 997 $arg_list[] = "'$arg_name' => $arg_value";
998 } 998 }
999 999
1000 $output = '<?php '; 1000 $output = '<?php ';
1001 1001
1002 if (isset($assign_var)) { 1002 if (isset($assign_var)) {
1003 $output .= "ob_start();\n"; 1003 $output .= "ob_start();\n";
1004 } 1004 }
1005 1005
1006 $output .= 1006 $output .=
1007 "\$_smarty_tpl_vars = \$this->_tpl_vars;\n"; 1007 "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
1008 1008
1009 1009
1010 $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))"; 1010 $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
1011 $output .= "\$this->_smarty_include($_params);\n" . 1011 $output .= "\$this->_smarty_include($_params);\n" .
1012 "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" . 1012 "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
1013 "unset(\$_smarty_tpl_vars);\n"; 1013 "unset(\$_smarty_tpl_vars);\n";
1014 1014
1015 if (isset($assign_var)) { 1015 if (isset($assign_var)) {
1016 $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n"; 1016 $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
1017 } 1017 }
1018 1018
1019 $output .= ' ?>'; 1019 $output .= ' ?>';
1020 1020
1021 return $output; 1021 return $output;
1022 1022
1023 } 1023 }
1024 1024
1025 /** 1025 /**
1026 * Compile {include ...} tag 1026 * Compile {include ...} tag
1027 * 1027 *
1028 * @param string $tag_args 1028 * @param string $tag_args
1029 * @return string 1029 * @return string
1030 */ 1030 */
1031 function _compile_include_php_tag($tag_args) 1031 function _compile_include_php_tag($tag_args)
1032 { 1032 {
1033 $attrs = $this->_parse_attrs($tag_args); 1033 $attrs = $this->_parse_attrs($tag_args);
1034 1034
1035 if (empty($attrs['file'])) { 1035 if (empty($attrs['file'])) {
1036 $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__); 1036 $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
1037 } 1037 }
1038 1038
1039 $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']); 1039 $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
1040 $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true'; 1040 $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
1041 1041
1042 $arg_list = array(); 1042 $arg_list = array();
1043 foreach($attrs as $arg_name => $arg_value) { 1043 foreach($attrs as $arg_name => $arg_value) {
1044 if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') { 1044 if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
1045 if(is_bool($arg_value)) 1045 if(is_bool($arg_value))
1046 $arg_value = $arg_value ? 'true' : 'false'; 1046 $arg_value = $arg_value ? 'true' : 'false';
1047 $arg_list[] = "'$arg_name' => $arg_value"; 1047 $arg_list[] = "'$arg_name' => $arg_value";
1048 } 1048 }
1049 } 1049 }
1050 1050
1051 $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))"; 1051 $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
1052 1052
1053 return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline; 1053 return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
1054 } 1054 }
1055 1055
1056 1056
1057 /** 1057 /**
1058 * Compile {section ...} tag 1058 * Compile {section ...} tag
1059 * 1059 *
1060 * @param string $tag_args 1060 * @param string $tag_args
1061 * @return string 1061 * @return string
1062 */ 1062 */
1063 function _compile_section_start($tag_args) 1063 function _compile_section_start($tag_args)
1064 { 1064 {
1065 $attrs = $this->_parse_attrs($tag_args); 1065 $attrs = $this->_parse_attrs($tag_args);
1066 $arg_list = array(); 1066 $arg_list = array();
1067 1067
1068 $output = '<?php '; 1068 $output = '<?php ';
1069 $section_name = $attrs['name']; 1069 $section_name = $attrs['name'];
1070 if (empty($section_name)) { 1070 if (empty($section_name)) {
1071 $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__); 1071 $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
1072 } 1072 }
1073 1073
1074 $output .= "unset(\$this->_sections[$section_name]);\n"; 1074 $output .= "unset(\$this->_sections[$section_name]);\n";
1075 $section_props = "\$this->_sections[$section_name]"; 1075 $section_props = "\$this->_sections[$section_name]";
1076 1076
1077 foreach ($attrs as $attr_name => $attr_value) { 1077 foreach ($attrs as $attr_name => $attr_value) {
1078 switch ($attr_name) { 1078 switch ($attr_name) {
1079 case 'loop': 1079 case 'loop':
1080 $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n"; 1080 $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
1081 break; 1081 break;
1082 1082
1083 case 'show': 1083 case 'show':
1084 if (is_bool($attr_value)) 1084 if (is_bool($attr_value))
1085 $show_attr_value = $attr_value ? 'true' : 'false'; 1085 $show_attr_value = $attr_value ? 'true' : 'false';
1086 else 1086 else
1087 $show_attr_value = "(bool)$attr_value"; 1087 $show_attr_value = "(bool)$attr_value";
1088 $output .= "{$section_props}['show'] = $show_attr_value;\n"; 1088 $output .= "{$section_props}['show'] = $show_attr_value;\n";
1089 break; 1089 break;
1090 1090
1091 case 'name': 1091 case 'name':
1092 $output .= "{$section_props}['$attr_name'] = $attr_value;\n"; 1092 $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
1093 break; 1093 break;
1094 1094
1095 case 'max': 1095 case 'max':
1096 case 'start': 1096 case 'start':
1097 $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n"; 1097 $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
1098 break; 1098 break;
1099 1099
1100 case 'step': 1100 case 'step':
1101 $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n"; 1101 $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
1102 break; 1102 break;
1103 1103
1104 default: 1104 default:
1105 $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__); 1105 $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
1106 break; 1106 break;
1107 } 1107 }
1108 } 1108 }
1109 1109
1110 if (!isset($attrs['show'])) 1110 if (!isset($attrs['show']))
1111 $output .= "{$section_props}['show'] = true;\n"; 1111 $output .= "{$section_props}['show'] = true;\n";
1112 1112
1113 if (!isset($attrs['loop'])) 1113 if (!isset($attrs['loop']))
1114 $output .= "{$section_props}['loop'] = 1;\n"; 1114 $output .= "{$section_props}['loop'] = 1;\n";
1115 1115
1116 if (!isset($attrs['max'])) 1116 if (!isset($attrs['max']))
1117 $output .= "{$section_props}['max'] = {$section_props}['loop'];\n"; 1117 $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
1118 else 1118 else
1119 $output .= "if ({$section_props}['max'] < 0)\n" . 1119 $output .= "if ({$section_props}['max'] < 0)\n" .
1120 " {$section_props}['max'] = {$section_props}['loop'];\n"; 1120 " {$section_props}['max'] = {$section_props}['loop'];\n";
1121 1121
1122 if (!isset($attrs['step'])) 1122 if (!isset($attrs['step']))
1123 $output .= "{$section_props}['step'] = 1;\n"; 1123 $output .= "{$section_props}['step'] = 1;\n";
1124 1124
1125 if (!isset($attrs['start'])) 1125 if (!isset($attrs['start']))
1126 $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n"; 1126 $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
1127 else { 1127 else {
1128 $output .= "if ({$section_props}['start'] < 0)\n" . 1128 $output .= "if ({$section_props}['start'] < 0)\n" .
1129 " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" . 1129 " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
1130 "else\n" . 1130 "else\n" .
1131 " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n"; 1131 " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
1132 } 1132 }
1133 1133
1134 $output .= "if ({$section_props}['show']) {\n"; 1134 $output .= "if ({$section_props}['show']) {\n";
1135 if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) { 1135 if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
1136 $output .= " {$section_props}['total'] = {$section_props}['loop'];\n"; 1136 $output .= " {$section_props}['total'] = {$section_props}['loop'];\n";
1137 } else { 1137 } else {
1138 $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"; 1138 $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";
1139 } 1139 }
1140 $output .= " if ({$section_props}['total'] == 0)\n" . 1140 $output .= " if ({$section_props}['total'] == 0)\n" .
1141 " {$section_props}['show'] = false;\n" . 1141 " {$section_props}['show'] = false;\n" .
1142 "} else\n" . 1142 "} else\n" .
1143 " {$section_props}['total'] = 0;\n"; 1143 " {$section_props}['total'] = 0;\n";
1144 1144
1145 $output .= "if ({$section_props}['show']):\n"; 1145 $output .= "if ({$section_props}['show']):\n";
1146 $output .= " 1146 $output .= "
1147 for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1; 1147 for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
1148 {$section_props}['iteration'] <= {$section_props}['total']; 1148 {$section_props}['iteration'] <= {$section_props}['total'];
1149 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n"; 1149 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
1150 $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n"; 1150 $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
1151 $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n"; 1151 $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
1152 $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n"; 1152 $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
1153 $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n"; 1153 $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n";
1154 $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n"; 1154 $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n";
1155 1155
1156 $output .= "?>"; 1156 $output .= "?>";
1157 1157
1158 return $output; 1158 return $output;
1159 } 1159 }
1160 1160
1161 1161
1162 /** 1162 /**
1163 * Compile {foreach ...} tag. 1163 * Compile {foreach ...} tag.
1164 * 1164 *
1165 * @param string $tag_args 1165 * @param string $tag_args
1166 * @return string 1166 * @return string
1167 */ 1167 */
1168 function _compile_foreach_start($tag_args) 1168 function _compile_foreach_start($tag_args)
1169 { 1169 {
1170 $attrs = $this->_parse_attrs($tag_args); 1170 $attrs = $this->_parse_attrs($tag_args);
1171 $arg_list = array(); 1171 $arg_list = array();
1172 1172
1173 if (empty($attrs['from'])) { 1173 if (empty($attrs['from'])) {
1174 return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__); 1174 return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
1175 } 1175 }
1176 $from = $attrs['from']; 1176 $from = $attrs['from'];
1177 1177
1178 if (empty($attrs['item'])) { 1178 if (empty($attrs['item'])) {
1179 return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__); 1179 return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
1180 } 1180 }
1181 $item = $this->_dequote($attrs['item']); 1181 $item = $this->_dequote($attrs['item']);
1182 if (!preg_match('~^\w+$~', $item)) { 1182 if (!preg_match('~^\w+$~', $item)) {
1183 return $this->_syntax_error("foreach: 'item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__); 1183 return $this->_syntax_error("foreach: 'item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1184 } 1184 }
1185 1185
1186 if (isset($attrs['key'])) { 1186 if (isset($attrs['key'])) {
1187 $key = $this->_dequote($attrs['key']); 1187 $key = $this->_dequote($attrs['key']);
1188 if (!preg_match('~^\w+$~', $key)) { 1188 if (!preg_match('~^\w+$~', $key)) {
1189 return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__); 1189 return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1190 } 1190 }
1191 $key_part = "\$this->_tpl_vars['$key'] => "; 1191 $key_part = "\$this->_tpl_vars['$key'] => ";
1192 } else { 1192 } else {
1193 $key = null; 1193 $key = null;
1194 $key_part = ''; 1194 $key_part = '';
1195 } 1195 }
1196 1196
1197 if (isset($attrs['name'])) { 1197 if (isset($attrs['name'])) {
1198 $name = $attrs['name']; 1198 $name = $attrs['name'];
1199 } else { 1199 } else {
1200 $name = null; 1200 $name = null;
1201 } 1201 }
1202 1202
1203 $output = '<?php '; 1203 $output = '<?php ';
1204 $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }"; 1204 $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
1205 if (isset($name)) { 1205 if (isset($name)) {
1206 $foreach_props = "\$this->_foreach[$name]"; 1206 $foreach_props = "\$this->_foreach[$name]";
1207 $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n"; 1207 $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
1208 $output .= "if ({$foreach_props}['total'] > 0):\n"; 1208 $output .= "if ({$foreach_props}['total'] > 0):\n";
1209 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n"; 1209 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1210 $output .= " {$foreach_props}['iteration']++;\n"; 1210 $output .= " {$foreach_props}['iteration']++;\n";
1211 } else { 1211 } else {
1212 $output .= "if (count(\$_from)):\n"; 1212 $output .= "if (count(\$_from)):\n";
1213 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n"; 1213 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1214 } 1214 }
1215 $output .= '?>'; 1215 $output .= '?>';
1216 1216
1217 return $output; 1217 return $output;
1218 } 1218 }
1219 1219
1220 1220
1221 /** 1221 /**
1222 * Compile {capture} .. {/capture} tags 1222 * Compile {capture} .. {/capture} tags
1223 * 1223 *
1224 * @param boolean $start true if this is the {capture} tag 1224 * @param boolean $start true if this is the {capture} tag
1225 * @param string $tag_args 1225 * @param string $tag_args
1226 * @return string 1226 * @return string
1227 */ 1227 */
1228 1228
1229 function _compile_capture_tag($start, $tag_args = '') 1229 function _compile_capture_tag($start, $tag_args = '')
1230 { 1230 {
1231 $attrs = $this->_parse_attrs($tag_args); 1231 $attrs = $this->_parse_attrs($tag_args);
1232 1232
1233 if ($start) { 1233 if ($start) {
1234 $buffer = isset($attrs['name']) ? $attrs['name'] : "'default'"; 1234 $buffer = isset($attrs['name']) ? $attrs['name'] : "'default'";
1235 $assign = isset($attrs['assign']) ? $attrs['assign'] : null; 1235 $assign = isset($attrs['assign']) ? $attrs['assign'] : null;
1236 $append = isset($attrs['append']) ? $attrs['append'] : null; 1236 $append = isset($attrs['append']) ? $attrs['append'] : null;
1237 1237
1238 $output = "<?php ob_start(); ?>"; 1238 $output = "<?php ob_start(); ?>";
1239 $this->_capture_stack[] = array($buffer, $assign, $append); 1239 $this->_capture_stack[] = array($buffer, $assign, $append);
1240 } else { 1240 } else {
1241 list($buffer, $assign, $append) = array_pop($this->_capture_stack); 1241 list($buffer, $assign, $append) = array_pop($this->_capture_stack);
1242 $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); "; 1242 $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
1243 if (isset($assign)) { 1243 if (isset($assign)) {
1244 $output .= " \$this->assign($assign, ob_get_contents());"; 1244 $output .= " \$this->assign($assign, ob_get_contents());";
1245 } 1245 }
1246 if (isset($append)) { 1246 if (isset($append)) {
1247 $output .= " \$this->append($append, ob_get_contents());"; 1247 $output .= " \$this->append($append, ob_get_contents());";
1248 } 1248 }
1249 $output .= "ob_end_clean(); ?>"; 1249 $output .= "ob_end_clean(); ?>";
1250 } 1250 }
1251 1251
1252 return $output; 1252 return $output;
1253 } 1253 }
1254 1254
1255 /** 1255 /**
1256 * Compile {if ...} tag 1256 * Compile {if ...} tag
1257 * 1257 *
1258 * @param string $tag_args 1258 * @param string $tag_args
1259 * @param boolean $elseif if true, uses elseif instead of if 1259 * @param boolean $elseif if true, uses elseif instead of if
1260 * @return string 1260 * @return string
1261 */ 1261 */
1262 function _compile_if_tag($tag_args, $elseif = false) 1262 function _compile_if_tag($tag_args, $elseif = false)
1263 { 1263 {
1264 1264
1265 /* Tokenize args for 'if' tag. */ 1265 /* Tokenize args for 'if' tag. */
1266 preg_match_all('~(?> 1266 preg_match_all('~(?>
1267 ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call 1267 ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
1268 ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)? | # var or quoted string 1268 ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)? | # var or quoted string
1269 \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token 1269 \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token
1270 \b\w+\b | # valid word token 1270 \b\w+\b | # valid word token
1271 \S+ # anything else 1271 \S+ # anything else
1272 )~x', $tag_args, $match); 1272 )~x', $tag_args, $match);
1273 1273
1274 $tokens = $match[0]; 1274 $tokens = $match[0];
1275 1275
1276 if(empty($tokens)) { 1276 if(empty($tokens)) {
1277 $_error_msg = $elseif ? "'elseif'" : "'if'"; 1277 $_error_msg = $elseif ? "'elseif'" : "'if'";
1278 $_error_msg .= ' statement requires arguments'; 1278 $_error_msg .= ' statement requires arguments';
1279 $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__); 1279 $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__);
1280 } 1280 }
1281 1281
1282 1282
1283 // make sure we have balanced parenthesis 1283 // make sure we have balanced parenthesis
1284 $token_count = array_count_values($tokens); 1284 $token_count = array_count_values($tokens);
1285 if(isset($token_count['(']) && $token_count['('] != $token_count[')']) { 1285 if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1286 $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__); 1286 $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
1287 } 1287 }
1288 1288
1289 $is_arg_stack = array(); 1289 $is_arg_stack = array();
1290 1290
1291 for ($i = 0; $i < count($tokens); $i++) { 1291 for ($i = 0; $i < count($tokens); $i++) {
1292 1292
1293 $token = &$tokens[$i]; 1293 $token = &$tokens[$i];
1294 1294
1295 switch (strtolower($token)) { 1295 switch (strtolower($token)) {
1296 case '!': 1296 case '!':
1297 case '%': 1297 case '%':
1298 case '!==': 1298 case '!==':
1299 case '==': 1299 case '==':
1300 case '===': 1300 case '===':
1301 case '>': 1301 case '>':
1302 case '<': 1302 case '<':
1303 case '!=': 1303 case '!=':
1304 case '<>': 1304 case '<>':
1305 case '<<': 1305 case '<<':
1306 case '>>': 1306 case '>>':
1307 case '<=': 1307 case '<=':
1308 case '>=': 1308 case '>=':
1309 case '&&': 1309 case '&&':
1310 case '||': 1310 case '||':
1311 case '|': 1311 case '|':
1312 case '^': 1312 case '^':
1313 case '&': 1313 case '&':
1314 case '~': 1314 case '~':
1315 case ')': 1315 case ')':
1316 case ',': 1316 case ',':
1317 case '+': 1317 case '+':
1318 case '-': 1318 case '-':
1319 case '*': 1319 case '*':
1320 case '/': 1320 case '/':
1321 case '@': 1321 case '@':
1322 break; 1322 break;
1323 1323
1324 case 'eq': 1324 case 'eq':
1325 $token = '=='; 1325 $token = '==';
1326 break; 1326 break;
1327 1327
1328 case 'ne': 1328 case 'ne':
1329 case 'neq': 1329 case 'neq':
1330 $token = '!='; 1330 $token = '!=';
1331 break; 1331 break;
1332 1332
1333 case 'lt': 1333 case 'lt':
1334 $token = '<'; 1334 $token = '<';
1335 break; 1335 break;
1336 1336
1337 case 'le': 1337 case 'le':
1338 case 'lte': 1338 case 'lte':
1339 $token = '<='; 1339 $token = '<=';
1340 break; 1340 break;
1341 1341
1342 case 'gt': 1342 case 'gt':
1343 $token = '>'; 1343 $token = '>';
1344 break; 1344 break;
1345 1345
1346 case 'ge': 1346 case 'ge':
1347 case 'gte': 1347 case 'gte':
1348 $token = '>='; 1348 $token = '>=';
1349 break; 1349 break;
1350 1350
1351 case 'and': 1351 case 'and':
1352 $token = '&&'; 1352 $token = '&&';
1353 break; 1353 break;
1354 1354
1355 case 'or': 1355 case 'or':
1356 $token = '||'; 1356 $token = '||';
1357 break; 1357 break;
1358 1358
1359 case 'not': 1359 case 'not':
1360 $token = '!'; 1360 $token = '!';
1361 break; 1361 break;
1362 1362
1363 case 'mod': 1363 case 'mod':
1364 $token = '%'; 1364 $token = '%';
1365 break; 1365 break;
1366 1366
1367 case '(': 1367 case '(':
1368 array_push($is_arg_stack, $i); 1368 array_push($is_arg_stack, $i);
1369 break; 1369 break;
1370 1370
1371 case 'is': 1371 case 'is':
1372 /* If last token was a ')', we operate on the parenthesized 1372 /* If last token was a ')', we operate on the parenthesized
1373 expression. The start of the expression is on the stack. 1373 expression. The start of the expression is on the stack.
1374 Otherwise, we operate on the last encountered token. */ 1374 Otherwise, we operate on the last encountered token. */
1375 if ($tokens[$i-1] == ')') { 1375 if ($tokens[$i-1] == ')') {
1376 $is_arg_start = array_pop($is_arg_stack); 1376 $is_arg_start = array_pop($is_arg_stack);
1377 if ($is_arg_start != 0) { 1377 if ($is_arg_start != 0) {
1378 if (preg_match('~^' . $this->_func_regexp . '$~', $tokens[$is_arg_start-1])) { 1378 if (preg_match('~^' . $this->_func_regexp . '$~', $tokens[$is_arg_start-1])) {
1379 $is_arg_start--; 1379 $is_arg_start--;
1380 } 1380 }
1381 } 1381 }
1382 } else 1382 } else
1383 $is_arg_start = $i-1; 1383 $is_arg_start = $i-1;
1384 /* Construct the argument for 'is' expression, so it knows 1384 /* Construct the argument for 'is' expression, so it knows
1385 what to operate on. */ 1385 what to operate on. */
1386 $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start)); 1386 $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1387 1387
1388 /* Pass all tokens from next one until the end to the 1388 /* Pass all tokens from next one until the end to the
1389 'is' expression parsing function. The function will 1389 'is' expression parsing function. The function will
1390 return modified tokens, where the first one is the result 1390 return modified tokens, where the first one is the result
1391 of the 'is' expression and the rest are the tokens it 1391 of the 'is' expression and the rest are the tokens it
1392 didn't touch. */ 1392 didn't touch. */
1393 $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1)); 1393 $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
1394 1394
1395 /* Replace the old tokens with the new ones. */ 1395 /* Replace the old tokens with the new ones. */
1396 array_splice($tokens, $is_arg_start, count($tokens), $new_tokens); 1396 array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1397 1397
1398 /* Adjust argument start so that it won't change from the 1398 /* Adjust argument start so that it won't change from the
1399 current position for the next iteration. */ 1399 current position for the next iteration. */
1400 $i = $is_arg_start; 1400 $i = $is_arg_start;
1401 break; 1401 break;
1402 1402
1403 default: 1403 default:
1404 if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) { 1404 if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) {
1405 // function call 1405 // function call
1406 if($this->security && 1406 if($this->security &&
1407 !in_array($token, $this->security_settings['IF_FUNCS'])) { 1407 !in_array($token, $this->security_settings['IF_FUNCS'])) {
1408 $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__); 1408 $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1409 } 1409 }
1410 } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && (strpos('+-*/^%&|', substr($token, -1)) === false) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') { 1410 } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && (strpos('+-*/^%&|', substr($token, -1)) === false) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
1411 // variable function call 1411 // variable function call
1412 $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__); 1412 $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1413 } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) { 1413 } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {
1414 // object or variable 1414 // object or variable
1415 $token = $this->_parse_var_props($token); 1415 $token = $this->_parse_var_props($token);
1416 } elseif(is_numeric($token)) { 1416 } elseif(is_numeric($token)) {
1417 // number, skip it 1417 // number, skip it
1418 } else { 1418 } else {
1419 $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__); 1419 $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
1420 } 1420 }
1421 break; 1421 break;
1422 } 1422 }
1423 } 1423 }
1424 1424
1425 if ($elseif) 1425 if ($elseif)
1426 return '<?php elseif ('.implode(' ', $tokens).'): ?>'; 1426 return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1427 else 1427 else
1428 return '<?php if ('.implode(' ', $tokens).'): ?>'; 1428 return '<?php if ('.implode(' ', $tokens).'): ?>';
1429 } 1429 }
1430 1430
1431 1431
1432 function _compile_arg_list($type, $name, $attrs, &$cache_code) { 1432 function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1433 $arg_list = array(); 1433 $arg_list = array();
1434 1434
1435 if (isset($type) && isset($name) 1435 if (isset($type) && isset($name)
1436 && isset($this->_plugins[$type]) 1436 && isset($this->_plugins[$type])
1437 && isset($this->_plugins[$type][$name]) 1437 && isset($this->_plugins[$type][$name])
1438 && empty($this->_plugins[$type][$name][4]) 1438 && empty($this->_plugins[$type][$name][4])
1439 && is_array($this->_plugins[$type][$name][5]) 1439 && is_array($this->_plugins[$type][$name][5])
1440 ) { 1440 ) {
1441 /* we have a list of parameters that should be cached */ 1441 /* we have a list of parameters that should be cached */
1442 $_cache_attrs = $this->_plugins[$type][$name][5]; 1442 $_cache_attrs = $this->_plugins[$type][$name][5];
1443 $_count = $this->_cache_attrs_count++; 1443 $_count = $this->_cache_attrs_count++;
1444 $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');"; 1444 $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1445 1445
1446 } else { 1446 } else {
1447 /* no parameters are cached */ 1447 /* no parameters are cached */
1448 $_cache_attrs = null; 1448 $_cache_attrs = null;
1449 } 1449 }
1450 1450
1451 foreach ($attrs as $arg_name => $arg_value) { 1451 foreach ($attrs as $arg_name => $arg_value) {
1452 if (is_bool($arg_value)) 1452 if (is_bool($arg_value))
1453 $arg_value = $arg_value ? 'true' : 'false'; 1453 $arg_value = $arg_value ? 'true' : 'false';
1454 if (is_null($arg_value)) 1454 if (is_null($arg_value))
1455 $arg_value = 'null'; 1455 $arg_value = 'null';
1456 if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) { 1456 if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1457 $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)"; 1457 $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1458 } else { 1458 } else {
1459 $arg_list[] = "'$arg_name' => $arg_value"; 1459 $arg_list[] = "'$arg_name' => $arg_value";
1460 } 1460 }
1461 } 1461 }
1462 return $arg_list; 1462 return $arg_list;
1463 } 1463 }
1464 1464
1465 /** 1465 /**
1466 * Parse is expression 1466 * Parse is expression
1467 * 1467 *
1468 * @param string $is_arg 1468 * @param string $is_arg
1469 * @param array $tokens 1469 * @param array $tokens
1470 * @return array 1470 * @return array
1471 */ 1471 */
1472 function _parse_is_expr($is_arg, $tokens) 1472 function _parse_is_expr($is_arg, $tokens)
1473 { 1473 {
1474 $expr_end = 0; 1474 $expr_end = 0;
1475 $negate_expr = false; 1475 $negate_expr = false;
1476 1476
1477 if (($first_token = array_shift($tokens)) == 'not') { 1477 if (($first_token = array_shift($tokens)) == 'not') {
1478 $negate_expr = true; 1478 $negate_expr = true;
1479 $expr_type = array_shift($tokens); 1479 $expr_type = array_shift($tokens);
1480 } else 1480 } else
1481 $expr_type = $first_token; 1481 $expr_type = $first_token;
1482 1482
1483 switch ($expr_type) { 1483 switch ($expr_type) {
1484 case 'even': 1484 case 'even':
1485 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') { 1485 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1486 $expr_end++; 1486 $expr_end++;
1487 $expr_arg = $tokens[$expr_end++]; 1487 $expr_arg = $tokens[$expr_end++];
1488 $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))"; 1488 $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1489 } else 1489 } else
1490 $expr = "!(1 & $is_arg)"; 1490 $expr = "!(1 & $is_arg)";
1491 break; 1491 break;
1492 1492
1493 case 'odd': 1493 case 'odd':
1494 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') { 1494 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1495 $expr_end++; 1495 $expr_end++;
1496 $expr_arg = $tokens[$expr_end++]; 1496 $expr_arg = $tokens[$expr_end++];
1497 $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))"; 1497 $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1498 } else 1498 } else
1499 $expr = "(1 & $is_arg)"; 1499 $expr = "(1 & $is_arg)";
1500 break; 1500 break;
1501 1501
1502 case 'div': 1502 case 'div':
1503 if (@$tokens[$expr_end] == 'by') { 1503 if (@$tokens[$expr_end] == 'by') {
1504 $expr_end++; 1504 $expr_end++;
1505 $expr_arg = $tokens[$expr_end++]; 1505 $expr_arg = $tokens[$expr_end++];
1506 $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")"; 1506 $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1507 } else { 1507 } else {
1508 $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__); 1508 $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1509 } 1509 }
1510 break; 1510 break;
1511 1511
1512 default: 1512 default:
1513 $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__); 1513 $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1514 break; 1514 break;
1515 } 1515 }
1516 1516
1517 if ($negate_expr) { 1517 if ($negate_expr) {
1518 $expr = "!($expr)"; 1518 $expr = "!($expr)";
1519 } 1519 }
1520 1520
1521 array_splice($tokens, 0, $expr_end, $expr); 1521 array_splice($tokens, 0, $expr_end, $expr);
1522 1522
1523 return $tokens; 1523 return $tokens;
1524 } 1524 }
1525 1525
1526 1526
1527 /** 1527 /**
1528 * Parse attribute string 1528 * Parse attribute string
1529 * 1529 *
1530 * @param string $tag_args 1530 * @param string $tag_args
1531 * @return array 1531 * @return array
1532 */ 1532 */
1533 function _parse_attrs($tag_args) 1533 function _parse_attrs($tag_args)
1534 { 1534 {
1535 1535
1536 /* Tokenize tag attributes. */ 1536 /* Tokenize tag attributes. */
1537 preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+) 1537 preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
1538 )+ | 1538 )+ |
1539 [=] 1539 [=]
1540 ~x', $tag_args, $match); 1540 ~x', $tag_args, $match);
1541 $tokens = $match[0]; 1541 $tokens = $match[0];
1542 1542
1543 $attrs = array(); 1543 $attrs = array();
1544 /* Parse state: 1544 /* Parse state:
1545 0 - expecting attribute name 1545 0 - expecting attribute name
1546 1 - expecting '=' 1546 1 - expecting '='
1547 2 - expecting attribute value (not '=') */ 1547 2 - expecting attribute value (not '=') */
1548 $state = 0; 1548 $state = 0;
1549 1549
1550 foreach ($tokens as $token) { 1550 foreach ($tokens as $token) {
1551 switch ($state) { 1551 switch ($state) {
1552 case 0: 1552 case 0:
1553 /* If the token is a valid identifier, we set attribute name 1553 /* If the token is a valid identifier, we set attribute name
1554 and go to state 1. */ 1554 and go to state 1. */
1555 if (preg_match('~^\w+$~', $token)) { 1555 if (preg_match('~^\w+$~', $token)) {
1556 $attr_name = $token; 1556 $attr_name = $token;
1557 $state = 1; 1557 $state = 1;
1558 } else 1558 } else
1559 $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__); 1559 $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1560 break; 1560 break;
1561 1561
1562 case 1: 1562 case 1:
1563 /* If the token is '=', then we go to state 2. */ 1563 /* If the token is '=', then we go to state 2. */
1564 if ($token == '=') { 1564 if ($token == '=') {
1565 $state = 2; 1565 $state = 2;
1566 } else 1566 } else
1567 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__); 1567 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1568 break; 1568 break;
1569 1569
1570 case 2: 1570 case 2:
1571 /* If token is not '=', we set the attribute value and go to 1571 /* If token is not '=', we set the attribute value and go to
1572 state 0. */ 1572 state 0. */
1573 if ($token != '=') { 1573 if ($token != '=') {
1574 /* We booleanize the token if it's a non-quoted possible 1574 /* We booleanize the token if it's a non-quoted possible
1575 boolean value. */ 1575 boolean value. */
1576 if (preg_match('~^(on|yes|true)$~', $token)) { 1576 if (preg_match('~^(on|yes|true)$~', $token)) {
1577 $token = 'true'; 1577 $token = 'true';
1578 } else if (preg_match('~^(off|no|false)$~', $token)) { 1578 } else if (preg_match('~^(off|no|false)$~', $token)) {
1579 $token = 'false'; 1579 $token = 'false';
1580 } else if ($token == 'null') { 1580 } else if ($token == 'null') {
1581 $token = 'null'; 1581 $token = 'null';
1582 } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) { 1582 } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
1583 /* treat integer literally */ 1583 /* treat integer literally */
1584 } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) { 1584 } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {
1585 /* treat as a string, double-quote it escaping quotes */ 1585 /* treat as a string, double-quote it escaping quotes */
1586 $token = '"'.addslashes($token).'"'; 1586 $token = '"'.addslashes($token).'"';
1587 } 1587 }
1588 1588
1589 $attrs[$attr_name] = $token; 1589 $attrs[$attr_name] = $token;
1590 $state = 0; 1590 $state = 0;
1591 } else 1591 } else
1592 $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__); 1592 $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1593 break; 1593 break;
1594 } 1594 }
1595 $last_token = $token; 1595 $last_token = $token;
1596 } 1596 }
1597 1597
1598 if($state != 0) { 1598 if($state != 0) {
1599 if($state == 1) { 1599 if($state == 1) {
1600 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__); 1600 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1601 } else { 1601 } else {
1602 $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__); 1602 $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
1603 } 1603 }
1604 } 1604 }
1605 1605
1606 $this->_parse_vars_props($attrs); 1606 $this->_parse_vars_props($attrs);
1607 1607
1608 return $attrs; 1608 return $attrs;
1609 } 1609 }
1610 1610
1611 /** 1611 /**
1612 * compile multiple variables and section properties tokens into 1612 * compile multiple variables and section properties tokens into
1613 * PHP code 1613 * PHP code
1614 * 1614 *
1615 * @param array $tokens 1615 * @param array $tokens
1616 */ 1616 */
1617 function _parse_vars_props(&$tokens) 1617 function _parse_vars_props(&$tokens)
1618 { 1618 {
1619 foreach($tokens as $key => $val) { 1619 foreach($tokens as $key => $val) {
1620 $tokens[$key] = $this->_parse_var_props($val); 1620 $tokens[$key] = $this->_parse_var_props($val);
1621 } 1621 }
1622 } 1622 }
1623 1623
1624 /** 1624 /**
1625 * compile single variable and section properties token into 1625 * compile single variable and section properties token into
1626 * PHP code 1626 * PHP code
1627 * 1627 *
1628 * @param string $val 1628 * @param string $val
1629 * @param string $tag_attrs 1629 * @param string $tag_attrs
1630 * @return string 1630 * @return string
1631 */ 1631 */
1632 function _parse_var_props($val) 1632 function _parse_var_props($val)
1633 { 1633 {
1634 $val = trim($val); 1634 $val = trim($val);
1635 1635
1636 if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) { 1636 if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {
1637 // $ variable or object 1637 // $ variable or object
1638 $return = $this->_parse_var($match[1]); 1638 $return = $this->_parse_var($match[1]);
1639 $modifiers = $match[2]; 1639 $modifiers = $match[2];
1640 if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) { 1640 if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
1641 $_default_mod_string = implode('|',(array)$this->default_modifiers); 1641 $_default_mod_string = implode('|',(array)$this->default_modifiers);
1642 $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers; 1642 $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
1643 } 1643 }
1644 $this->_parse_modifiers($return, $modifiers); 1644 $this->_parse_modifiers($return, $modifiers);
1645 return $return; 1645 return $return;
1646 } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { 1646 } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1647 // double quoted text 1647 // double quoted text
1648 preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match); 1648 preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1649 $return = $this->_expand_quoted_text($match[1]); 1649 $return = $this->_expand_quoted_text($match[1]);
1650 if($match[2] != '') { 1650 if($match[2] != '') {
1651 $this->_parse_modifiers($return, $match[2]); 1651 $this->_parse_modifiers($return, $match[2]);
1652 } 1652 }
1653 return $return; 1653 return $return;
1654 } 1654 }
1655 elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { 1655 elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1656 // numerical constant 1656 // numerical constant
1657 preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match); 1657 preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1658 if($match[2] != '') { 1658 if($match[2] != '') {
1659 $this->_parse_modifiers($match[1], $match[2]); 1659 $this->_parse_modifiers($match[1], $match[2]);
1660 return $match[1]; 1660 return $match[1];
1661 } 1661 }
1662 } 1662 }
1663 elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { 1663 elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1664 // single quoted text 1664 // single quoted text
1665 preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match); 1665 preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1666 if($match[2] != '') { 1666 if($match[2] != '') {
1667 $this->_parse_modifiers($match[1], $match[2]); 1667 $this->_parse_modifiers($match[1], $match[2]);
1668 return $match[1]; 1668 return $match[1];
1669 } 1669 }
1670 } 1670 }
1671 elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { 1671 elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1672 // config var 1672 // config var
1673 return $this->_parse_conf_var($val); 1673 return $this->_parse_conf_var($val);
1674 } 1674 }
1675 elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { 1675 elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1676 // section var 1676 // section var
1677 return $this->_parse_section_prop($val); 1677 return $this->_parse_section_prop($val);
1678 } 1678 }
1679 elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) { 1679 elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
1680 // literal string 1680 // literal string
1681 return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"'); 1681 return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"');
1682 } 1682 }
1683 return $val; 1683 return $val;
1684 } 1684 }
1685 1685
1686 /** 1686 /**
1687 * expand quoted text with embedded variables 1687 * expand quoted text with embedded variables
1688 * 1688 *
1689 * @param string $var_expr 1689 * @param string $var_expr
1690 * @return string 1690 * @return string
1691 */ 1691 */
1692 function _expand_quoted_text($var_expr) 1692 function _expand_quoted_text($var_expr)
1693 { 1693 {
1694 // if contains unescaped $, expand it 1694 // if contains unescaped $, expand it
1695 if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) { 1695 if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) {
1696 $_match = $_match[0]; 1696 $_match = $_match[0];
1697 $_replace = array(); 1697 $_replace = array();
1698 foreach($_match as $_var) { 1698 foreach($_match as $_var) {
1699 $_replace[$_var] = '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."'; 1699 $_replace[$_var] = '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."';
1700 } 1700 }
1701 $var_expr = strtr($var_expr, $_replace); 1701 $var_expr = strtr($var_expr, $_replace);
1702 $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr); 1702 $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr);
1703 } else { 1703 } else {
1704 $_return = $var_expr; 1704 $_return = $var_expr;
1705 } 1705 }
1706 // replace double quoted literal string with single quotes 1706 // replace double quoted literal string with single quotes
1707 $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return); 1707 $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return);
1708 // escape dollar sign if not printing a var
1709 $_return = preg_replace('~\$(\W)~',"\\\\\$\\1",$_return);
1708 return $_return; 1710 return $_return;
1709 } 1711 }
1710 1712
1711 /** 1713 /**
1712 * parse variable expression into PHP code 1714 * parse variable expression into PHP code
1713 * 1715 *
1714 * @param string $var_expr 1716 * @param string $var_expr
1715 * @param string $output 1717 * @param string $output
1716 * @return string 1718 * @return string
1717 */ 1719 */
1718 function _parse_var($var_expr) 1720 function _parse_var($var_expr)
1719 { 1721 {
1720 $_has_math = false; 1722 $_has_math = false;
1721 $_has_php4_method_chaining = false; 1723 $_has_php4_method_chaining = false;
1722 $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE); 1724 $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
1723 1725
1724 if(count($_math_vars) > 1) { 1726 if(count($_math_vars) > 1) {
1725 $_first_var = ""; 1727 $_first_var = "";
1726 $_complete_var = ""; 1728 $_complete_var = "";
1727 $_output = ""; 1729 $_output = "";
1728 // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter) 1730 // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
1729 foreach($_math_vars as $_k => $_math_var) { 1731 foreach($_math_vars as $_k => $_math_var) {
1730 $_math_var = $_math_vars[$_k]; 1732 $_math_var = $_math_vars[$_k];
1731 1733
1732 if(!empty($_math_var) || is_numeric($_math_var)) { 1734 if(!empty($_math_var) || is_numeric($_math_var)) {
1733 // hit a math operator, so process the stuff which came before it 1735 // hit a math operator, so process the stuff which came before it
1734 if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) { 1736 if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
1735 $_has_math = true; 1737 $_has_math = true;
1736 if(!empty($_complete_var) || is_numeric($_complete_var)) { 1738 if(!empty($_complete_var) || is_numeric($_complete_var)) {
1737 $_output .= $this->_parse_var($_complete_var); 1739 $_output .= $this->_parse_var($_complete_var);
1738 } 1740 }
1739 1741
1740 // just output the math operator to php 1742 // just output the math operator to php
1741 $_output .= $_math_var; 1743 $_output .= $_math_var;
1742 1744
1743 if(empty($_first_var)) 1745 if(empty($_first_var))
1744 $_first_var = $_complete_var; 1746 $_first_var = $_complete_var;
1745 1747
1746 $_complete_var = ""; 1748 $_complete_var = "";
1747 } else { 1749 } else {
1748 $_complete_var .= $_math_var; 1750 $_complete_var .= $_math_var;
1749 } 1751 }
1750 } 1752 }
1751 } 1753 }
1752 if($_has_math) { 1754 if($_has_math) {
1753 if(!empty($_complete_var) || is_numeric($_complete_var)) 1755 if(!empty($_complete_var) || is_numeric($_complete_var))
1754 $_output .= $this->_parse_var($_complete_var); 1756 $_output .= $this->_parse_var($_complete_var);
1755 1757
1756 // get the modifiers working (only the last var from math + modifier is left) 1758 // get the modifiers working (only the last var from math + modifier is left)
1757 $var_expr = $_complete_var; 1759 $var_expr = $_complete_var;
1758 } 1760 }
1759 } 1761 }
1760 1762
1761 // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit) 1763 // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
1762 if(is_numeric(substr($var_expr, 0, 1))) 1764 if(is_numeric(substr($var_expr, 0, 1)))
1763 $_var_ref = $var_expr; 1765 $_var_ref = $var_expr;
1764 else 1766 else
1765 $_var_ref = substr($var_expr, 1); 1767 $_var_ref = substr($var_expr, 1);
1766 1768
1767 if(!$_has_math) { 1769 if(!$_has_math) {
1768 1770
1769 // get [foo] and .foo and ->foo and (...) pieces 1771 // get [foo] and .foo and ->foo and (...) pieces
1770 preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match); 1772 preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match);
1771 1773
1772 $_indexes = $match[0]; 1774 $_indexes = $match[0];
1773 $_var_name = array_shift($_indexes); 1775 $_var_name = array_shift($_indexes);
1774 1776
1775 /* Handle $smarty.* variable references as a special case. */ 1777 /* Handle $smarty.* variable references as a special case. */
1776 if ($_var_name == 'smarty') { 1778 if ($_var_name == 'smarty') {
1777 /* 1779 /*
1778 * If the reference could be compiled, use the compiled output; 1780 * If the reference could be compiled, use the compiled output;
1779 * otherwise, fall back on the $smarty variable generated at 1781 * otherwise, fall back on the $smarty variable generated at
1780 * run-time. 1782 * run-time.
1781 */ 1783 */
1782 if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) { 1784 if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
1783 $_output = $smarty_ref; 1785 $_output = $smarty_ref;
1784 } else { 1786 } else {
1785 $_var_name = substr(array_shift($_indexes), 1); 1787 $_var_name = substr(array_shift($_indexes), 1);
1786 $_output = "\$this->_smarty_vars['$_var_name']"; 1788 $_output = "\$this->_smarty_vars['$_var_name']";
1787 } 1789 }
1788 } elseif(is_numeric($_var_name) && is_numeric(substr($var_expr, 0, 1))) { 1790 } elseif(is_numeric($_var_name) && is_numeric(substr($var_expr, 0, 1))) {
1789 // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers 1791 // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
1790 if(count($_indexes) > 0) 1792 if(count($_indexes) > 0)
1791 { 1793 {
1792 $_var_name .= implode("", $_indexes); 1794 $_var_name .= implode("", $_indexes);
1793 $_indexes = array(); 1795 $_indexes = array();
1794 } 1796 }
1795 $_output = $_var_name; 1797 $_output = $_var_name;
1796 } else { 1798 } else {
1797 $_output = "\$this->_tpl_vars['$_var_name']"; 1799 $_output = "\$this->_tpl_vars['$_var_name']";
1798 } 1800 }
1799 1801
1800 foreach ($_indexes as $_index) { 1802 foreach ($_indexes as $_index) {
1801 if (substr($_index, 0, 1) == '[') { 1803 if (substr($_index, 0, 1) == '[') {
1802 $_index = substr($_index, 1, -1); 1804 $_index = substr($_index, 1, -1);
1803 if (is_numeric($_index)) { 1805 if (is_numeric($_index)) {
1804 $_output .= "[$_index]"; 1806 $_output .= "[$_index]";
1805 } elseif (substr($_index, 0, 1) == '$') { 1807 } elseif (substr($_index, 0, 1) == '$') {
1806 if (strpos($_index, '.') !== false) { 1808 if (strpos($_index, '.') !== false) {
1807 $_output .= '[' . $this->_parse_var($_index) . ']'; 1809 $_output .= '[' . $this->_parse_var($_index) . ']';
1808 } else { 1810 } else {
1809 $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]"; 1811 $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
1810 } 1812 }
1811 } else { 1813 } else {
1812 $_var_parts = explode('.', $_index); 1814 $_var_parts = explode('.', $_index);
1813 $_var_section = $_var_parts[0]; 1815 $_var_section = $_var_parts[0];
1814 $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index'; 1816 $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
1815 $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]"; 1817 $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
1816 } 1818 }
1817 } else if (substr($_index, 0, 1) == '.') { 1819 } else if (substr($_index, 0, 1) == '.') {
1818 if (substr($_index, 1, 1) == '$') 1820 if (substr($_index, 1, 1) == '$')
1819 $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]"; 1821 $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
1820 else 1822 else
1821 $_output .= "['" . substr($_index, 1) . "']"; 1823 $_output .= "['" . substr($_index, 1) . "']";
1822 } else if (substr($_index,0,2) == '->') { 1824 } else if (substr($_index,0,2) == '->') {
1823 if(substr($_index,2,2) == '__') { 1825 if(substr($_index,2,2) == '__') {
1824 $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__); 1826 $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1825 } elseif($this->security && substr($_index, 2, 1) == '_') { 1827 } elseif($this->security && substr($_index, 2, 1) == '_') {
1826 $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__); 1828 $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1827 } elseif (substr($_index, 2, 1) == '$') { 1829 } elseif (substr($_index, 2, 1) == '$') {
1828 if ($this->security) { 1830 if ($this->security) {
1829 $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__); 1831 $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1830 } else { 1832 } else {
1831 $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}'; 1833 $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
1832 } 1834 }
1833 } else { 1835 } else {
1834 if ($this->_phpversion < 5) { 1836 if ($this->_phpversion < 5) {
1835 $_has_php4_method_chaining = true; 1837 $_has_php4_method_chaining = true;
1836 $_output .= "; \$_foo = \$_foo"; 1838 $_output .= "; \$_foo = \$_foo";
1837 } 1839 }
1838 $_output .= $_index; 1840 $_output .= $_index;
1839 } 1841 }
1840 } elseif (substr($_index, 0, 1) == '(') { 1842 } elseif (substr($_index, 0, 1) == '(') {
1841 $_index = $this->_parse_parenth_args($_index); 1843 $_index = $this->_parse_parenth_args($_index);
1842 $_output .= $_index; 1844 $_output .= $_index;
1843 } else { 1845 } else {
1844 $_output .= $_index; 1846 $_output .= $_index;
1845 } 1847 }
1846 } 1848 }
1847 } 1849 }
1848 1850
1849 if ($_has_php4_method_chaining) { 1851 if ($_has_php4_method_chaining) {
1850 $_tmp = str_replace("'","\'",'$_foo = '.$_output.'; return $_foo;'); 1852 $_tmp = str_replace("'","\'",'$_foo = '.$_output.'; return $_foo;');
1851 return "eval('".$_tmp."')"; 1853 return "eval('".$_tmp."')";
1852 } else { 1854 } else {
1853 return $_output; 1855 return $_output;
1854 } 1856 }
1855 } 1857 }
1856 1858
1857 /** 1859 /**
1858 * parse arguments in function call parenthesis 1860 * parse arguments in function call parenthesis
1859 * 1861 *
1860 * @param string $parenth_args 1862 * @param string $parenth_args
1861 * @return string 1863 * @return string
1862 */ 1864 */
1863 function _parse_parenth_args($parenth_args) 1865 function _parse_parenth_args($parenth_args)
1864 { 1866 {
1865 preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match); 1867 preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match);
1866 $orig_vals = $match = $match[0]; 1868 $orig_vals = $match = $match[0];
1867 $this->_parse_vars_props($match); 1869 $this->_parse_vars_props($match);
1868 $replace = array(); 1870 $replace = array();
1869 for ($i = 0, $count = count($match); $i < $count; $i++) { 1871 for ($i = 0, $count = count($match); $i < $count; $i++) {
1870 $replace[$orig_vals[$i]] = $match[$i]; 1872 $replace[$orig_vals[$i]] = $match[$i];
1871 } 1873 }
1872 return strtr($parenth_args, $replace); 1874 return strtr($parenth_args, $replace);
1873 } 1875 }
1874 1876
1875 /** 1877 /**
1876 * parse configuration variable expression into PHP code 1878 * parse configuration variable expression into PHP code
1877 * 1879 *
1878 * @param string $conf_var_expr 1880 * @param string $conf_var_expr
1879 */ 1881 */
1880 function _parse_conf_var($conf_var_expr) 1882 function _parse_conf_var($conf_var_expr)
1881 { 1883 {
1882 $parts = explode('|', $conf_var_expr, 2); 1884 $parts = explode('|', $conf_var_expr, 2);
1883 $var_ref = $parts[0]; 1885 $var_ref = $parts[0];
1884 $modifiers = isset($parts[1]) ? $parts[1] : ''; 1886 $modifiers = isset($parts[1]) ? $parts[1] : '';
1885 1887
1886 $var_name = substr($var_ref, 1, -1); 1888 $var_name = substr($var_ref, 1, -1);
1887 1889
1888 $output = "\$this->_config[0]['vars']['$var_name']"; 1890 $output = "\$this->_config[0]['vars']['$var_name']";
1889 1891
1890 $this->_parse_modifiers($output, $modifiers); 1892 $this->_parse_modifiers($output, $modifiers);
1891 1893
1892 return $output; 1894 return $output;
1893 } 1895 }
1894 1896
1895 /** 1897 /**
1896 * parse section property expression into PHP code 1898 * parse section property expression into PHP code
1897 * 1899 *
1898 * @param string $section_prop_expr 1900 * @param string $section_prop_expr
1899 * @return string 1901 * @return string
1900 */ 1902 */
1901 function _parse_section_prop($section_prop_expr) 1903 function _parse_section_prop($section_prop_expr)
1902 { 1904 {
1903 $parts = explode('|', $section_prop_expr, 2); 1905 $parts = explode('|', $section_prop_expr, 2);
1904 $var_ref = $parts[0]; 1906 $var_ref = $parts[0];
1905 $modifiers = isset($parts[1]) ? $parts[1] : ''; 1907 $modifiers = isset($parts[1]) ? $parts[1] : '';
1906 1908
1907 preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match); 1909 preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1908 $section_name = $match[1]; 1910 $section_name = $match[1];
1909 $prop_name = $match[2]; 1911 $prop_name = $match[2];
1910 1912
1911 $output = "\$this->_sections['$section_name']['$prop_name']"; 1913 $output = "\$this->_sections['$section_name']['$prop_name']";
1912 1914
1913 $this->_parse_modifiers($output, $modifiers); 1915 $this->_parse_modifiers($output, $modifiers);
1914 1916
1915 return $output; 1917 return $output;
1916 } 1918 }
1917 1919
1918 1920
1919 /** 1921 /**
1920 * parse modifier chain into PHP code 1922 * parse modifier chain into PHP code
1921 * 1923 *
1922 * sets $output to parsed modified chain 1924 * sets $output to parsed modified chain
1923 * @param string $output 1925 * @param string $output
1924 * @param string $modifier_string 1926 * @param string $modifier_string
1925 */ 1927 */
1926 function _parse_modifiers(&$output, $modifier_string) 1928 function _parse_modifiers(&$output, $modifier_string)
1927 { 1929 {
1928 preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match); 1930 preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
1929 list(, $_modifiers, $modifier_arg_strings) = $_match; 1931 list(, $_modifiers, $modifier_arg_strings) = $_match;
1930 1932
1931 for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) { 1933 for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
1932 $_modifier_name = $_modifiers[$_i]; 1934 $_modifier_name = $_modifiers[$_i];
1933 1935
1934 if($_modifier_name == 'smarty') { 1936 if($_modifier_name == 'smarty') {
1935 // skip smarty modifier 1937 // skip smarty modifier
1936 continue; 1938 continue;
1937 } 1939 }
1938 1940
1939 preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match); 1941 preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
1940 $_modifier_args = $_match[1]; 1942 $_modifier_args = $_match[1];
1941 1943
1942 if (substr($_modifier_name, 0, 1) == '@') { 1944 if (substr($_modifier_name, 0, 1) == '@') {
1943 $_map_array = false; 1945 $_map_array = false;
1944 $_modifier_name = substr($_modifier_name, 1); 1946 $_modifier_name = substr($_modifier_name, 1);
1945 } else { 1947 } else {
1946 $_map_array = true; 1948 $_map_array = true;
1947 } 1949 }
1948 1950
1949 if (empty($this->_plugins['modifier'][$_modifier_name]) 1951 if (empty($this->_plugins['modifier'][$_modifier_name])
1950 && !$this->_get_plugin_filepath('modifier', $_modifier_name) 1952 && !$this->_get_plugin_filepath('modifier', $_modifier_name)
1951 && function_exists($_modifier_name)) { 1953 && function_exists($_modifier_name)) {
1952 if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) { 1954 if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
1953 $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__); 1955 $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
1954 } else { 1956 } else {
1955 $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false); 1957 $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false);
1956 } 1958 }
1957 } 1959 }
1958 $this->_add_plugin('modifier', $_modifier_name); 1960 $this->_add_plugin('modifier', $_modifier_name);
1959 1961
1960 $this->_parse_vars_props($_modifier_args); 1962 $this->_parse_vars_props($_modifier_args);
1961 1963
1962 if($_modifier_name == 'default') { 1964 if($_modifier_name == 'default') {
1963 // supress notifications of default modifier vars and args 1965 // supress notifications of default modifier vars and args
1964 if(substr($output, 0, 1) == '$') { 1966 if(substr($output, 0, 1) == '$') {
1965 $output = '@' . $output; 1967 $output = '@' . $output;
1966 } 1968 }
1967 if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') { 1969 if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') {
1968 $_modifier_args[0] = '@' . $_modifier_args[0]; 1970 $_modifier_args[0] = '@' . $_modifier_args[0];
1969 } 1971 }
1970 } 1972