diff options
author | Sébastien SAUVAGE <sebsauvage@sebsauvage.net> | 2013-02-26 10:09:41 +0100 |
---|---|---|
committer | Sébastien SAUVAGE <sebsauvage@sebsauvage.net> | 2013-02-26 10:09:41 +0100 |
commit | 450342737ced8ef2864b4f83a4107a7fafcc4add (patch) | |
tree | a6d2fe843c27b5c3d88dab5581b6a06828d544b9 /inc/rain.tpl.class.php | |
download | Shaarli-450342737ced8ef2864b4f83a4107a7fafcc4add.tar.gz Shaarli-450342737ced8ef2864b4f83a4107a7fafcc4add.tar.zst Shaarli-450342737ced8ef2864b4f83a4107a7fafcc4add.zip |
Initial commit (version 0.0.40 beta)v0.0.40beta
Diffstat (limited to 'inc/rain.tpl.class.php')
-rw-r--r-- | inc/rain.tpl.class.php | 1037 |
1 files changed, 1037 insertions, 0 deletions
diff --git a/inc/rain.tpl.class.php b/inc/rain.tpl.class.php new file mode 100644 index 00000000..e9f3bd29 --- /dev/null +++ b/inc/rain.tpl.class.php | |||
@@ -0,0 +1,1037 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * RainTPL | ||
5 | * ------- | ||
6 | * Realized by Federico Ulfo & maintained by the Rain Team | ||
7 | * Distributed under GNU/LGPL 3 License | ||
8 | * | ||
9 | * @version 2.7 | ||
10 | */ | ||
11 | |||
12 | |||
13 | class RainTPL{ | ||
14 | |||
15 | // ------------------------- | ||
16 | // CONFIGURATION | ||
17 | // ------------------------- | ||
18 | |||
19 | /** | ||
20 | * Template directory | ||
21 | * | ||
22 | * @var string | ||
23 | */ | ||
24 | static $tpl_dir = "tpl/"; | ||
25 | |||
26 | |||
27 | /** | ||
28 | * Cache directory. Is the directory where RainTPL will compile the template and save the cache | ||
29 | * | ||
30 | * @var string | ||
31 | */ | ||
32 | static $cache_dir = "tmp/"; | ||
33 | |||
34 | |||
35 | /** | ||
36 | * Template base URL. RainTPL will add this URL to the relative paths of element selected in $path_replace_list. | ||
37 | * | ||
38 | * @var string | ||
39 | */ | ||
40 | static $base_url = null; | ||
41 | |||
42 | |||
43 | /** | ||
44 | * Template extension. | ||
45 | * | ||
46 | * @var string | ||
47 | */ | ||
48 | static $tpl_ext = "html"; | ||
49 | |||
50 | |||
51 | /** | ||
52 | * Path replace is a cool features that replace all relative paths of images (<img src="...">), stylesheet (<link href="...">), script (<script src="...">) and link (<a href="...">) | ||
53 | * Set true to enable the path replace. | ||
54 | * | ||
55 | * @var unknown_type | ||
56 | */ | ||
57 | static $path_replace = true; | ||
58 | |||
59 | |||
60 | /** | ||
61 | * You can set what the path_replace method will replace. | ||
62 | * Avaible options: a, img, link, script, input | ||
63 | * | ||
64 | * @var array | ||
65 | */ | ||
66 | static $path_replace_list = array( 'a', 'img', 'link', 'script', 'input' ); | ||
67 | |||
68 | |||
69 | /** | ||
70 | * You can define in the black list what string are disabled into the template tags | ||
71 | * | ||
72 | * @var unknown_type | ||
73 | */ | ||
74 | static $black_list = array( '\$this', 'raintpl::', 'self::', '_SESSION', '_SERVER', '_ENV', 'eval', 'exec', 'unlink', 'rmdir' ); | ||
75 | |||
76 | |||
77 | /** | ||
78 | * Check template. | ||
79 | * true: checks template update time, if changed it compile them | ||
80 | * false: loads the compiled template. Set false if server doesn't have write permission for cache_directory. | ||
81 | * | ||
82 | */ | ||
83 | static $check_template_update = true; | ||
84 | |||
85 | |||
86 | /** | ||
87 | * PHP tags <? ?> | ||
88 | * True: php tags are enabled into the template | ||
89 | * False: php tags are disabled into the template and rendered as html | ||
90 | * | ||
91 | * @var bool | ||
92 | */ | ||
93 | static $php_enabled = false; | ||
94 | |||
95 | |||
96 | /** | ||
97 | * Debug mode flag. | ||
98 | * True: debug mode is used, syntax errors are displayed directly in template. Execution of script is not terminated. | ||
99 | * False: exception is thrown on found error. | ||
100 | * | ||
101 | * @var bool | ||
102 | */ | ||
103 | static $debug = false; | ||
104 | |||
105 | // ------------------------- | ||
106 | |||
107 | |||
108 | // ------------------------- | ||
109 | // RAINTPL VARIABLES | ||
110 | // ------------------------- | ||
111 | |||
112 | /** | ||
113 | * Is the array where RainTPL keep the variables assigned | ||
114 | * | ||
115 | * @var array | ||
116 | */ | ||
117 | public $var = array(); | ||
118 | |||
119 | protected $tpl = array(), // variables to keep the template directories and info | ||
120 | $cache = false, // static cache enabled / disabled | ||
121 | $cache_id = null; // identify only one cache | ||
122 | |||
123 | protected static $config_name_sum = array(); // takes all the config to create the md5 of the file | ||
124 | |||
125 | // ------------------------- | ||
126 | |||
127 | |||
128 | |||
129 | const CACHE_EXPIRE_TIME = 3600; // default cache expire time = hour | ||
130 | |||
131 | |||
132 | |||
133 | /** | ||
134 | * Assign variable | ||
135 | * eg. $t->assign('name','mickey'); | ||
136 | * | ||
137 | * @param mixed $variable_name Name of template variable or associative array name/value | ||
138 | * @param mixed $value value assigned to this variable. Not set if variable_name is an associative array | ||
139 | */ | ||
140 | |||
141 | function assign( $variable, $value = null ){ | ||
142 | if( is_array( $variable ) ) | ||
143 | $this->var += $variable; | ||
144 | else | ||
145 | $this->var[ $variable ] = $value; | ||
146 | } | ||
147 | |||
148 | |||
149 | |||
150 | /** | ||
151 | * Draw the template | ||
152 | * eg. $html = $tpl->draw( 'demo', TRUE ); // return template in string | ||
153 | * or $tpl->draw( $tpl_name ); // echo the template | ||
154 | * | ||
155 | * @param string $tpl_name template to load | ||
156 | * @param boolean $return_string true=return a string, false=echo the template | ||
157 | * @return string | ||
158 | */ | ||
159 | |||
160 | function draw( $tpl_name, $return_string = false ){ | ||
161 | |||
162 | try { | ||
163 | // compile the template if necessary and set the template filepath | ||
164 | $this->check_template( $tpl_name ); | ||
165 | } catch (RainTpl_Exception $e) { | ||
166 | $output = $this->printDebug($e); | ||
167 | die($output); | ||
168 | } | ||
169 | |||
170 | // Cache is off and, return_string is false | ||
171 | // Rain just echo the template | ||
172 | |||
173 | if( !$this->cache && !$return_string ){ | ||
174 | extract( $this->var ); | ||
175 | include $this->tpl['compiled_filename']; | ||
176 | unset( $this->tpl ); | ||
177 | } | ||
178 | |||
179 | |||
180 | // cache or return_string are enabled | ||
181 | // rain get the output buffer to save the output in the cache or to return it as string | ||
182 | |||
183 | else{ | ||
184 | |||
185 | //---------------------- | ||
186 | // get the output buffer | ||
187 | //---------------------- | ||
188 | ob_start(); | ||
189 | extract( $this->var ); | ||
190 | include $this->tpl['compiled_filename']; | ||
191 | $raintpl_contents = ob_get_clean(); | ||
192 | //---------------------- | ||
193 | |||
194 | |||
195 | // save the output in the cache | ||
196 | if( $this->cache ) | ||
197 | file_put_contents( $this->tpl['cache_filename'], "<?php if(!class_exists('raintpl')){exit;}?>" . $raintpl_contents ); | ||
198 | |||
199 | // free memory | ||
200 | unset( $this->tpl ); | ||
201 | |||
202 | // return or print the template | ||
203 | if( $return_string ) return $raintpl_contents; else echo $raintpl_contents; | ||
204 | |||
205 | } | ||
206 | |||
207 | } | ||
208 | |||
209 | |||
210 | |||
211 | /** | ||
212 | * If exists a valid cache for this template it returns the cache | ||
213 | * | ||
214 | * @param string $tpl_name Name of template (set the same of draw) | ||
215 | * @param int $expiration_time Set after how many seconds the cache expire and must be regenerated | ||
216 | * @return string it return the HTML or null if the cache must be recreated | ||
217 | */ | ||
218 | |||
219 | function cache( $tpl_name, $expire_time = self::CACHE_EXPIRE_TIME, $cache_id = null ){ | ||
220 | |||
221 | // set the cache_id | ||
222 | $this->cache_id = $cache_id; | ||
223 | |||
224 | if( !$this->check_template( $tpl_name ) && file_exists( $this->tpl['cache_filename'] ) && ( time() - filemtime( $this->tpl['cache_filename'] ) < $expire_time ) ) | ||
225 | return substr( file_get_contents( $this->tpl['cache_filename'] ), 43 ); | ||
226 | else{ | ||
227 | //delete the cache of the selected template | ||
228 | if (file_exists($this->tpl['cache_filename'])) | ||
229 | unlink($this->tpl['cache_filename'] ); | ||
230 | $this->cache = true; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | |||
235 | |||
236 | /** | ||
237 | * Configure the settings of RainTPL | ||
238 | * | ||
239 | */ | ||
240 | static function configure( $setting, $value = null ){ | ||
241 | if( is_array( $setting ) ) | ||
242 | foreach( $setting as $key => $value ) | ||
243 | self::configure( $key, $value ); | ||
244 | else if( property_exists( __CLASS__, $setting ) ){ | ||
245 | self::$$setting = $value; | ||
246 | self::$config_name_sum[$key] = $value; // take trace of all config | ||
247 | } | ||
248 | } | ||
249 | |||
250 | |||
251 | |||
252 | // check if has to compile the template | ||
253 | // return true if the template has changed | ||
254 | protected function check_template( $tpl_name ){ | ||
255 | |||
256 | if( !isset($this->tpl['checked']) ){ | ||
257 | |||
258 | $tpl_basename = basename( $tpl_name ); // template basename | ||
259 | $tpl_basedir = strpos($tpl_name,"/") ? dirname($tpl_name) . '/' : null; // template basedirectory | ||
260 | $tpl_dir = self::$tpl_dir . $tpl_basedir; // template directory | ||
261 | $this->tpl['tpl_filename'] = $tpl_dir . $tpl_basename . '.' . self::$tpl_ext; // template filename | ||
262 | $temp_compiled_filename = self::$cache_dir . $tpl_basename . "." . md5( $tpl_dir . implode('', self::$config_name_sum)); | ||
263 | $this->tpl['compiled_filename'] = $temp_compiled_filename . '.rtpl.php'; // cache filename | ||
264 | $this->tpl['cache_filename'] = $temp_compiled_filename . '.s_' . $this->cache_id . '.rtpl.php'; // static cache filename | ||
265 | |||
266 | // if the template doesn't exsist throw an error | ||
267 | if( self::$check_template_update && !file_exists( $this->tpl['tpl_filename'] ) ){ | ||
268 | $e = new RainTpl_NotFoundException( 'Template '. $tpl_basename .' not found!' ); | ||
269 | throw $e->setTemplateFile($this->tpl['tpl_filename']); | ||
270 | } | ||
271 | |||
272 | // file doesn't exsist, or the template was updated, Rain will compile the template | ||
273 | if( !file_exists( $this->tpl['compiled_filename'] ) || ( self::$check_template_update && filemtime($this->tpl['compiled_filename']) < filemtime( $this->tpl['tpl_filename'] ) ) ){ | ||
274 | $this->compileFile( $tpl_basename, $tpl_basedir, $this->tpl['tpl_filename'], self::$cache_dir, $this->tpl['compiled_filename'] ); | ||
275 | return true; | ||
276 | } | ||
277 | $this->tpl['checked'] = true; | ||
278 | } | ||
279 | } | ||
280 | |||
281 | |||
282 | /** | ||
283 | * execute stripslaches() on the xml block. Invoqued by preg_replace_callback function below | ||
284 | * @access protected | ||
285 | */ | ||
286 | protected function xml_reSubstitution($capture) { | ||
287 | return "<?php echo '<?xml ".stripslashes($capture[1])." ?>'; ?>"; | ||
288 | } | ||
289 | |||
290 | /** | ||
291 | * Compile and write the compiled template file | ||
292 | * @access protected | ||
293 | */ | ||
294 | protected function compileFile( $tpl_basename, $tpl_basedir, $tpl_filename, $cache_dir, $compiled_filename ){ | ||
295 | |||
296 | //read template file | ||
297 | $this->tpl['source'] = $template_code = file_get_contents( $tpl_filename ); | ||
298 | |||
299 | //xml substitution | ||
300 | $template_code = preg_replace( "/<\?xml(.*?)\?>/s", "##XML\\1XML##", $template_code ); | ||
301 | |||
302 | //disable php tag | ||
303 | if( !self::$php_enabled ) | ||
304 | $template_code = str_replace( array("<?","?>"), array("<?","?>"), $template_code ); | ||
305 | |||
306 | //xml re-substitution | ||
307 | $template_code = preg_replace_callback ( "/##XML(.*?)XML##/s", array($this, 'xml_reSubstitution'), $template_code ); | ||
308 | |||
309 | //compile template | ||
310 | $template_compiled = "<?php if(!class_exists('raintpl')){exit;}?>" . $this->compileTemplate( $template_code, $tpl_basedir ); | ||
311 | |||
312 | |||
313 | // fix the php-eating-newline-after-closing-tag-problem | ||
314 | $template_compiled = str_replace( "?>\n", "?>\n\n", $template_compiled ); | ||
315 | |||
316 | // create directories | ||
317 | if( !is_dir( $cache_dir ) ) | ||
318 | mkdir( $cache_dir, 0755, true ); | ||
319 | |||
320 | if( !is_writable( $cache_dir ) ) | ||
321 | throw new RainTpl_Exception ('Cache directory ' . $cache_dir . 'doesn\'t have write permission. Set write permission or set RAINTPL_CHECK_TEMPLATE_UPDATE to false. More details on http://www.raintpl.com/Documentation/Documentation-for-PHP-developers/Configuration/'); | ||
322 | |||
323 | //write compiled file | ||
324 | file_put_contents( $compiled_filename, $template_compiled ); | ||
325 | } | ||
326 | |||
327 | |||
328 | |||
329 | /** | ||
330 | * Compile template | ||
331 | * @access protected | ||
332 | */ | ||
333 | protected function compileTemplate( $template_code, $tpl_basedir ){ | ||
334 | |||
335 | //tag list | ||
336 | $tag_regexp = array( 'loop' => '(\{loop(?: name){0,1}="\${0,1}[^"]*"\})', | ||
337 | 'loop_close' => '(\{\/loop\})', | ||
338 | 'if' => '(\{if(?: condition){0,1}="[^"]*"\})', | ||
339 | 'elseif' => '(\{elseif(?: condition){0,1}="[^"]*"\})', | ||
340 | 'else' => '(\{else\})', | ||
341 | 'if_close' => '(\{\/if\})', | ||
342 | 'function' => '(\{function="[^"]*"\})', | ||
343 | 'noparse' => '(\{noparse\})', | ||
344 | 'noparse_close'=> '(\{\/noparse\})', | ||
345 | 'ignore' => '(\{ignore\})', | ||
346 | 'ignore_close' => '(\{\/ignore\})', | ||
347 | 'include' => '(\{include="[^"]*"(?: cache="[^"]*")?\})', | ||
348 | 'template_info'=> '(\{\$template_info\})', | ||
349 | 'function' => '(\{function="(\w*?)(?:.*?)"\})' | ||
350 | ); | ||
351 | |||
352 | $tag_regexp = "/" . join( "|", $tag_regexp ) . "/"; | ||
353 | |||
354 | //split the code with the tags regexp | ||
355 | $template_code = preg_split ( $tag_regexp, $template_code, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); | ||
356 | |||
357 | //path replace (src of img, background and href of link) | ||
358 | $template_code = $this->path_replace( $template_code, $tpl_basedir ); | ||
359 | |||
360 | //compile the code | ||
361 | $compiled_code = $this->compileCode( $template_code ); | ||
362 | |||
363 | //return the compiled code | ||
364 | return $compiled_code; | ||
365 | |||
366 | } | ||
367 | |||
368 | |||
369 | |||
370 | /** | ||
371 | * Compile the code | ||
372 | * @access protected | ||
373 | */ | ||
374 | protected function compileCode( $parsed_code ){ | ||
375 | |||
376 | //variables initialization | ||
377 | $compiled_code = $open_if = $comment_is_open = $ignore_is_open = null; | ||
378 | $loop_level = 0; | ||
379 | |||
380 | //read all parsed code | ||
381 | while( $html = array_shift( $parsed_code ) ){ | ||
382 | |||
383 | //close ignore tag | ||
384 | if( !$comment_is_open && strpos( $html, '{/ignore}' ) !== FALSE ) | ||
385 | $ignore_is_open = false; | ||
386 | |||
387 | //code between tag ignore id deleted | ||
388 | elseif( $ignore_is_open ){ | ||
389 | //ignore the code | ||
390 | } | ||
391 | |||
392 | //close no parse tag | ||
393 | elseif( strpos( $html, '{/noparse}' ) !== FALSE ) | ||
394 | $comment_is_open = false; | ||
395 | |||
396 | //code between tag noparse is not compiled | ||
397 | elseif( $comment_is_open ) | ||
398 | $compiled_code .= $html; | ||
399 | |||
400 | //ignore | ||
401 | elseif( strpos( $html, '{ignore}' ) !== FALSE ) | ||
402 | $ignore_is_open = true; | ||
403 | |||
404 | //noparse | ||
405 | elseif( strpos( $html, '{noparse}' ) !== FALSE ) | ||
406 | $comment_is_open = true; | ||
407 | |||
408 | //include tag | ||
409 | elseif( preg_match( '/\{include="([^"]*)"(?: cache="([^"]*)"){0,1}\}/', $html, $code ) ){ | ||
410 | |||
411 | //variables substitution | ||
412 | $include_var = $this->var_replace( $code[ 1 ], $left_delimiter = null, $right_delimiter = null, $php_left_delimiter = '".' , $php_right_delimiter = '."', $loop_level ); | ||
413 | |||
414 | // if the cache is active | ||
415 | if( isset($code[ 2 ]) ){ | ||
416 | |||
417 | //dynamic include | ||
418 | $compiled_code .= '<?php $tpl = new RainTpl;' . | ||
419 | 'if( $cache = $tpl->cache( $template = basename("'.$include_var.'") ) )' . | ||
420 | ' echo $cache;' . | ||
421 | 'else{' . | ||
422 | ' $tpl_dir_temp = self::$tpl_dir;' . | ||
423 | ' $tpl->assign( $this->var );' . | ||
424 | ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ). | ||
425 | ' $tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'. | ||
426 | '} ?>'; | ||
427 | } | ||
428 | else{ | ||
429 | |||
430 | //dynamic include | ||
431 | $compiled_code .= '<?php $tpl = new RainTpl;' . | ||
432 | '$tpl_dir_temp = self::$tpl_dir;' . | ||
433 | '$tpl->assign( $this->var );' . | ||
434 | ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ). | ||
435 | '$tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'. | ||
436 | '?>'; | ||
437 | |||
438 | |||
439 | } | ||
440 | |||
441 | } | ||
442 | |||
443 | //loop | ||
444 | elseif( preg_match( '/\{loop(?: name){0,1}="\${0,1}([^"]*)"\}/', $html, $code ) ){ | ||
445 | |||
446 | //increase the loop counter | ||
447 | $loop_level++; | ||
448 | |||
449 | //replace the variable in the loop | ||
450 | $var = $this->var_replace( '$' . $code[ 1 ], $tag_left_delimiter=null, $tag_right_delimiter=null, $php_left_delimiter=null, $php_right_delimiter=null, $loop_level-1 ); | ||
451 | |||
452 | //loop variables | ||
453 | $counter = "\$counter$loop_level"; // count iteration | ||
454 | $key = "\$key$loop_level"; // key | ||
455 | $value = "\$value$loop_level"; // value | ||
456 | |||
457 | //loop code | ||
458 | $compiled_code .= "<?php $counter=-1; if( isset($var) && is_array($var) && sizeof($var) ) foreach( $var as $key => $value ){ $counter++; ?>"; | ||
459 | |||
460 | } | ||
461 | |||
462 | //close loop tag | ||
463 | elseif( strpos( $html, '{/loop}' ) !== FALSE ) { | ||
464 | |||
465 | //iterator | ||
466 | $counter = "\$counter$loop_level"; | ||
467 | |||
468 | //decrease the loop counter | ||
469 | $loop_level--; | ||
470 | |||
471 | //close loop code | ||
472 | $compiled_code .= "<?php } ?>"; | ||
473 | |||
474 | } | ||
475 | |||
476 | //if | ||
477 | elseif( preg_match( '/\{if(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){ | ||
478 | |||
479 | //increase open if counter (for intendation) | ||
480 | $open_if++; | ||
481 | |||
482 | //tag | ||
483 | $tag = $code[ 0 ]; | ||
484 | |||
485 | //condition attribute | ||
486 | $condition = $code[ 1 ]; | ||
487 | |||
488 | // check if there's any function disabled by black_list | ||
489 | $this->function_check( $tag ); | ||
490 | |||
491 | //variable substitution into condition (no delimiter into the condition) | ||
492 | $parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level ); | ||
493 | |||
494 | //if code | ||
495 | $compiled_code .= "<?php if( $parsed_condition ){ ?>"; | ||
496 | |||
497 | } | ||
498 | |||
499 | //elseif | ||
500 | elseif( preg_match( '/\{elseif(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){ | ||
501 | |||
502 | //tag | ||
503 | $tag = $code[ 0 ]; | ||
504 | |||
505 | //condition attribute | ||
506 | $condition = $code[ 1 ]; | ||
507 | |||
508 | //variable substitution into condition (no delimiter into the condition) | ||
509 | $parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level ); | ||
510 | |||
511 | //elseif code | ||
512 | $compiled_code .= "<?php }elseif( $parsed_condition ){ ?>"; | ||
513 | } | ||
514 | |||
515 | //else | ||
516 | elseif( strpos( $html, '{else}' ) !== FALSE ) { | ||
517 | |||
518 | //else code | ||
519 | $compiled_code .= '<?php }else{ ?>'; | ||
520 | |||
521 | } | ||
522 | |||
523 | //close if tag | ||
524 | elseif( strpos( $html, '{/if}' ) !== FALSE ) { | ||
525 | |||
526 | //decrease if counter | ||
527 | $open_if--; | ||
528 | |||
529 | // close if code | ||
530 | $compiled_code .= '<?php } ?>'; | ||
531 | |||
532 | } | ||
533 | |||
534 | //function | ||
535 | elseif( preg_match( '/\{function="(\w*)(.*?)"\}/', $html, $code ) ){ | ||
536 | |||
537 | //tag | ||
538 | $tag = $code[ 0 ]; | ||
539 | |||
540 | //function | ||
541 | $function = $code[ 1 ]; | ||
542 | |||
543 | // check if there's any function disabled by black_list | ||
544 | $this->function_check( $tag ); | ||
545 | |||
546 | if( empty( $code[ 2 ] ) ) | ||
547 | $parsed_function = $function . "()"; | ||
548 | else | ||
549 | // parse the function | ||
550 | $parsed_function = $function . $this->var_replace( $code[ 2 ], $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level ); | ||
551 | |||
552 | //if code | ||
553 | $compiled_code .= "<?php echo $parsed_function; ?>"; | ||
554 | } | ||
555 | |||
556 | // show all vars | ||
557 | elseif ( strpos( $html, '{$template_info}' ) !== FALSE ) { | ||
558 | |||
559 | //tag | ||
560 | $tag = '{$template_info}'; | ||
561 | |||
562 | //if code | ||
563 | $compiled_code .= '<?php echo "<pre>"; print_r( $this->var ); echo "</pre>"; ?>'; | ||
564 | } | ||
565 | |||
566 | |||
567 | //all html code | ||
568 | else{ | ||
569 | |||
570 | //variables substitution (es. {$title}) | ||
571 | $html = $this->var_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true ); | ||
572 | //const substitution (es. {#CONST#}) | ||
573 | $html = $this->const_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true ); | ||
574 | //functions substitution (es. {"string"|functions}) | ||
575 | $compiled_code .= $this->func_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true ); | ||
576 | } | ||
577 | } | ||
578 | |||
579 | if( $open_if > 0 ) { | ||
580 | $e = new RainTpl_SyntaxException('Error! You need to close an {if} tag in ' . $this->tpl['tpl_filename'] . ' template'); | ||
581 | throw $e->setTemplateFile($this->tpl['tpl_filename']); | ||
582 | } | ||
583 | return $compiled_code; | ||
584 | } | ||
585 | |||
586 | |||
587 | |||
588 | protected function reduce_path( $path ){ | ||
589 | $path = str_replace( "//", "/", $path ); | ||
590 | return preg_replace('/\w+\/\.\.\//', '', $path ); | ||
591 | } | ||
592 | |||
593 | |||
594 | |||
595 | /** | ||
596 | * replace the path of image src, link href and a href. | ||
597 | * url => template_dir/url | ||
598 | * url# => url | ||
599 | * http://url => http://url | ||
600 | * | ||
601 | * @param string $html | ||
602 | * @return string html sostituito | ||
603 | */ | ||
604 | protected function path_replace( $html, $tpl_basedir ){ | ||
605 | |||
606 | if( self::$path_replace ){ | ||
607 | |||
608 | $tpl_dir = self::$base_url . self::$tpl_dir . $tpl_basedir; | ||
609 | |||
610 | // reduce the path | ||
611 | $path = $this->reduce_path($tpl_dir); | ||
612 | |||
613 | $exp = $sub = array(); | ||
614 | |||
615 | if( in_array( "img", self::$path_replace_list ) ){ | ||
616 | $exp = array( '/<img(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<img(.*?)src=(?:")([^"]+?)#(?:")/i', '/<img(.*?)src="(.*?)"/', '/<img(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ); | ||
617 | $sub = array( '<img$1src=@$2://$3@', '<img$1src=@$2@', '<img$1src="' . $path . '$2"', '<img$1src="$2"' ); | ||
618 | } | ||
619 | |||
620 | if( in_array( "script", self::$path_replace_list ) ){ | ||
621 | $exp = array_merge( $exp , array( '/<script(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<script(.*?)src=(?:")([^"]+?)#(?:")/i', '/<script(.*?)src="(.*?)"/', '/<script(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) ); | ||
622 | $sub = array_merge( $sub , array( '<script$1src=@$2://$3@', '<script$1src=@$2@', '<script$1src="' . $path . '$2"', '<script$1src="$2"' ) ); | ||
623 | } | ||
624 | |||
625 | if( in_array( "link", self::$path_replace_list ) ){ | ||
626 | $exp = array_merge( $exp , array( '/<link(.*?)href=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<link(.*?)href=(?:")([^"]+?)#(?:")/i', '/<link(.*?)href="(.*?)"/', '/<link(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) ); | ||
627 | $sub = array_merge( $sub , array( '<link$1href=@$2://$3@', '<link$1href=@$2@' , '<link$1href="' . $path . '$2"', '<link$1href="$2"' ) ); | ||
628 | } | ||
629 | |||
630 | if( in_array( "a", self::$path_replace_list ) ){ | ||
631 | $exp = array_merge( $exp , array( '/<a(.*?)href=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<a(.*?)href="(.*?)"/', '/<a(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) ); | ||
632 | $sub = array_merge( $sub , array( '<a$1href=@$2://$3@', '<a$1href="' . self::$base_url . '$2"', '<a$1href="$2"' ) ); | ||
633 | } | ||
634 | |||
635 | if( in_array( "input", self::$path_replace_list ) ){ | ||
636 | $exp = array_merge( $exp , array( '/<input(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<input(.*?)src=(?:")([^"]+?)#(?:")/i', '/<input(.*?)src="(.*?)"/', '/<input(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) ); | ||
637 | $sub = array_merge( $sub , array( '<input$1src=@$2://$3@', '<input$1src=@$2@', '<input$1src="' . $path . '$2"', '<input$1src="$2"' ) ); | ||
638 | } | ||
639 | |||
640 | return preg_replace( $exp, $sub, $html ); | ||
641 | |||
642 | } | ||
643 | else | ||
644 | return $html; | ||
645 | |||
646 | } | ||
647 | |||
648 | |||
649 | |||
650 | |||
651 | |||
652 | // replace const | ||
653 | function const_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){ | ||
654 | // const | ||
655 | return preg_replace( '/\{\#(\w+)\#{0,1}\}/', $php_left_delimiter . ( $echo ? " echo " : null ) . '\\1' . $php_right_delimiter, $html ); | ||
656 | } | ||
657 | |||
658 | |||
659 | |||
660 | // replace functions/modifiers on constants and strings | ||
661 | function func_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){ | ||
662 | |||
663 | preg_match_all( '/' . '\{\#{0,1}(\"{0,1}.*?\"{0,1})(\|\w.*?)\#{0,1}\}' . '/', $html, $matches ); | ||
664 | |||
665 | for( $i=0, $n=count($matches[0]); $i<$n; $i++ ){ | ||
666 | |||
667 | //complete tag ex: {$news.title|substr:0,100} | ||
668 | $tag = $matches[ 0 ][ $i ]; | ||
669 | |||
670 | //variable name ex: news.title | ||
671 | $var = $matches[ 1 ][ $i ]; | ||
672 | |||
673 | //function and parameters associate to the variable ex: substr:0,100 | ||
674 | $extra_var = $matches[ 2 ][ $i ]; | ||
675 | |||
676 | // check if there's any function disabled by black_list | ||
677 | $this->function_check( $tag ); | ||
678 | |||
679 | $extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level ); | ||
680 | |||
681 | |||
682 | // check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value | ||
683 | $is_init_variable = preg_match( "/^(\s*?)\=[^=](.*?)$/", $extra_var ); | ||
684 | |||
685 | //function associate to variable | ||
686 | $function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null; | ||
687 | |||
688 | //variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title) | ||
689 | $temp = preg_split( "/\.|\[|\-\>/", $var ); | ||
690 | |||
691 | //variable name | ||
692 | $var_name = $temp[ 0 ]; | ||
693 | |||
694 | //variable path | ||
695 | $variable_path = substr( $var, strlen( $var_name ) ); | ||
696 | |||
697 | //parentesis transform [ e ] in [" e in "] | ||
698 | $variable_path = str_replace( '[', '["', $variable_path ); | ||
699 | $variable_path = str_replace( ']', '"]', $variable_path ); | ||
700 | |||
701 | //transform .$variable in ["$variable"] | ||
702 | $variable_path = preg_replace('/\.\$(\w+)/', '["$\\1"]', $variable_path ); | ||
703 | |||
704 | //transform [variable] in ["variable"] | ||
705 | $variable_path = preg_replace('/\.(\w+)/', '["\\1"]', $variable_path ); | ||
706 | |||
707 | //if there's a function | ||
708 | if( $function_var ){ | ||
709 | |||
710 | // check if there's a function or a static method and separate, function by parameters | ||
711 | $function_var = str_replace("::", "@double_dot@", $function_var ); | ||
712 | |||
713 | // get the position of the first : | ||
714 | if( $dot_position = strpos( $function_var, ":" ) ){ | ||
715 | |||
716 | // get the function and the parameters | ||
717 | $function = substr( $function_var, 0, $dot_position ); | ||
718 | $params = substr( $function_var, $dot_position+1 ); | ||
719 | |||
720 | } | ||
721 | else{ | ||
722 | |||
723 | //get the function | ||
724 | $function = str_replace( "@double_dot@", "::", $function_var ); | ||
725 | $params = null; | ||
726 | |||
727 | } | ||
728 | |||
729 | // replace back the @double_dot@ with :: | ||
730 | $function = str_replace( "@double_dot@", "::", $function ); | ||
731 | $params = str_replace( "@double_dot@", "::", $params ); | ||
732 | |||
733 | |||
734 | } | ||
735 | else | ||
736 | $function = $params = null; | ||
737 | |||
738 | $php_var = $var_name . $variable_path; | ||
739 | |||
740 | // compile the variable for php | ||
741 | if( isset( $function ) ){ | ||
742 | if( $php_var ) | ||
743 | $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter; | ||
744 | else | ||
745 | $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $params ) )" : "$function()" ) . $php_right_delimiter; | ||
746 | } | ||
747 | else | ||
748 | $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter; | ||
749 | |||
750 | $html = str_replace( $tag, $php_var, $html ); | ||
751 | |||
752 | } | ||
753 | |||
754 | return $html; | ||
755 | |||
756 | } | ||
757 | |||
758 | |||
759 | |||
760 | function var_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){ | ||
761 | |||
762 | //all variables | ||
763 | if( preg_match_all( '/' . $tag_left_delimiter . '\$(\w+(?:\.\${0,1}[A-Za-z0-9_]+)*(?:(?:\[\${0,1}[A-Za-z0-9_]+\])|(?:\-\>\${0,1}[A-Za-z0-9_]+))*)(.*?)' . $tag_right_delimiter . '/', $html, $matches ) ){ | ||
764 | |||
765 | for( $parsed=array(), $i=0, $n=count($matches[0]); $i<$n; $i++ ) | ||
766 | $parsed[$matches[0][$i]] = array('var'=>$matches[1][$i],'extra_var'=>$matches[2][$i]); | ||
767 | |||
768 | foreach( $parsed as $tag => $array ){ | ||
769 | |||
770 | //variable name ex: news.title | ||
771 | $var = $array['var']; | ||
772 | |||
773 | //function and parameters associate to the variable ex: substr:0,100 | ||
774 | $extra_var = $array['extra_var']; | ||
775 | |||
776 | // check if there's any function disabled by black_list | ||
777 | $this->function_check( $tag ); | ||
778 | |||
779 | $extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level ); | ||
780 | |||
781 | // check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value | ||
782 | $is_init_variable = preg_match( "/^[a-z_A-Z\.\[\](\-\>)]*=[^=]*$/", $extra_var ); | ||
783 | |||
784 | //function associate to variable | ||
785 | $function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null; | ||
786 | |||
787 | //variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title) | ||
788 | $temp = preg_split( "/\.|\[|\-\>/", $var ); | ||
789 | |||
790 | //variable name | ||
791 | $var_name = $temp[ 0 ]; | ||
792 | |||
793 | //variable path | ||
794 | $variable_path = substr( $var, strlen( $var_name ) ); | ||
795 | |||
796 | //parentesis transform [ e ] in [" e in "] | ||
797 | $variable_path = str_replace( '[', '["', $variable_path ); | ||
798 | $variable_path = str_replace( ']', '"]', $variable_path ); | ||
799 | |||
800 | //transform .$variable in ["$variable"] and .variable in ["variable"] | ||
801 | $variable_path = preg_replace('/\.(\${0,1}\w+)/', '["\\1"]', $variable_path ); | ||
802 | |||
803 | // if is an assignment also assign the variable to $this->var['value'] | ||
804 | if( $is_init_variable ) | ||
805 | $extra_var = "=\$this->var['{$var_name}']{$variable_path}" . $extra_var; | ||
806 | |||
807 | |||
808 | |||
809 | //if there's a function | ||
810 | if( $function_var ){ | ||
811 | |||
812 | // check if there's a function or a static method and separate, function by parameters | ||
813 | $function_var = str_replace("::", "@double_dot@", $function_var ); | ||
814 | |||
815 | |||
816 | // get the position of the first : | ||
817 | if( $dot_position = strpos( $function_var, ":" ) ){ | ||
818 | |||
819 | // get the function and the parameters | ||
820 | $function = substr( $function_var, 0, $dot_position ); | ||
821 | $params = substr( $function_var, $dot_position+1 ); | ||
822 | |||
823 | } | ||
824 | else{ | ||
825 | |||
826 | //get the function | ||
827 | $function = str_replace( "@double_dot@", "::", $function_var ); | ||
828 | $params = null; | ||
829 | |||
830 | } | ||
831 | |||
832 | // replace back the @double_dot@ with :: | ||
833 | $function = str_replace( "@double_dot@", "::", $function ); | ||
834 | $params = str_replace( "@double_dot@", "::", $params ); | ||
835 | } | ||
836 | else | ||
837 | $function = $params = null; | ||
838 | |||
839 | //if it is inside a loop | ||
840 | if( $loop_level ){ | ||
841 | //verify the variable name | ||
842 | if( $var_name == 'key' ) | ||
843 | $php_var = '$key' . $loop_level; | ||
844 | elseif( $var_name == 'value' ) | ||
845 | $php_var = '$value' . $loop_level . $variable_path; | ||
846 | elseif( $var_name == 'counter' ) | ||
847 | $php_var = '$counter' . $loop_level; | ||
848 | else | ||
849 | $php_var = '$' . $var_name . $variable_path; | ||
850 | }else | ||
851 | $php_var = '$' . $var_name . $variable_path; | ||
852 | |||
853 | // compile the variable for php | ||
854 | if( isset( $function ) ) | ||
855 | $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter; | ||
856 | else | ||
857 | $php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter; | ||
858 | |||
859 | $html = str_replace( $tag, $php_var, $html ); | ||
860 | |||
861 | |||
862 | } | ||
863 | } | ||
864 | |||
865 | return $html; | ||
866 | } | ||
867 | |||
868 | |||
869 | |||
870 | /** | ||
871 | * Check if function is in black list (sandbox) | ||
872 | * | ||
873 | * @param string $code | ||
874 | * @param string $tag | ||
875 | */ | ||
876 | protected function function_check( $code ){ | ||
877 | |||
878 | $preg = '#(\W|\s)' . implode( '(\W|\s)|(\W|\s)', self::$black_list ) . '(\W|\s)#'; | ||
879 | |||
880 | // check if the function is in the black list (or not in white list) | ||
881 | if( count(self::$black_list) && preg_match( $preg, $code, $match ) ){ | ||
882 | |||
883 | // find the line of the error | ||
884 | $line = 0; | ||
885 | $rows=explode("\n",$this->tpl['source']); | ||
886 | while( !strpos($rows[$line],$code) ) | ||
887 | $line++; | ||
888 | |||
889 | // stop the execution of the script | ||
890 | $e = new RainTpl_SyntaxException('Unallowed syntax in ' . $this->tpl['tpl_filename'] . ' template'); | ||
891 | throw $e->setTemplateFile($this->tpl['tpl_filename']) | ||
892 | ->setTag($code) | ||
893 | ->setTemplateLine($line); | ||
894 | } | ||
895 | |||
896 | } | ||
897 | |||
898 | /** | ||
899 | * Prints debug info about exception or passes it further if debug is disabled. | ||
900 | * | ||
901 | * @param RainTpl_Exception $e | ||
902 | * @return string | ||
903 | */ | ||
904 | protected function printDebug(RainTpl_Exception $e){ | ||
905 | if (!self::$debug) { | ||
906 | throw $e; | ||
907 | } | ||
908 | $output = sprintf('<h2>Exception: %s</h2><h3>%s</h3><p>template: %s</p>', | ||
909 | get_class($e), | ||
910 | $e->getMessage(), | ||
911 | $e->getTemplateFile() | ||
912 | ); | ||
913 | if ($e instanceof RainTpl_SyntaxException) { | ||
914 | if (null != $e->getTemplateLine()) { | ||
915 | $output .= '<p>line: ' . $e->getTemplateLine() . '</p>'; | ||
916 | } | ||
917 | if (null != $e->getTag()) { | ||
918 | $output .= '<p>in tag: ' . htmlspecialchars($e->getTag()) . '</p>'; | ||
919 | } | ||
920 | if (null != $e->getTemplateLine() && null != $e->getTag()) { | ||
921 | $rows=explode("\n", htmlspecialchars($this->tpl['source'])); | ||
922 | $rows[$e->getTemplateLine()] = '<font color=red>' . $rows[$e->getTemplateLine()] . '</font>'; | ||
923 | $output .= '<h3>template code</h3>' . implode('<br />', $rows) . '</pre>'; | ||
924 | } | ||
925 | } | ||
926 | $output .= sprintf('<h3>trace</h3><p>In %s on line %d</p><pre>%s</pre>', | ||
927 | $e->getFile(), $e->getLine(), | ||
928 | nl2br(htmlspecialchars($e->getTraceAsString())) | ||
929 | ); | ||
930 | return $output; | ||
931 | } | ||
932 | } | ||
933 | |||
934 | |||
935 | /** | ||
936 | * Basic Rain tpl exception. | ||
937 | */ | ||
938 | class RainTpl_Exception extends Exception{ | ||
939 | /** | ||
940 | * Path of template file with error. | ||
941 | */ | ||
942 | protected $templateFile = ''; | ||
943 | |||
944 | /** | ||
945 | * Returns path of template file with error. | ||
946 | * | ||
947 | * @return string | ||
948 | */ | ||
949 | public function getTemplateFile() | ||
950 | { | ||
951 | return $this->templateFile; | ||
952 | } | ||
953 | |||
954 | /** | ||
955 | * Sets path of template file with error. | ||
956 | * | ||
957 | * @param string $templateFile | ||
958 | * @return RainTpl_Exception | ||
959 | */ | ||
960 | public function setTemplateFile($templateFile) | ||
961 | { | ||
962 | $this->templateFile = (string) $templateFile; | ||
963 | return $this; | ||
964 | } | ||
965 | } | ||
966 | |||
967 | /** | ||
968 | * Exception thrown when template file does not exists. | ||
969 | */ | ||
970 | class RainTpl_NotFoundException extends RainTpl_Exception{ | ||
971 | } | ||
972 | |||
973 | /** | ||
974 | * Exception thrown when syntax error occurs. | ||
975 | */ | ||
976 | class RainTpl_SyntaxException extends RainTpl_Exception{ | ||
977 | /** | ||
978 | * Line in template file where error has occured. | ||
979 | * | ||
980 | * @var int | null | ||
981 | */ | ||
982 | protected $templateLine = null; | ||
983 | |||
984 | /** | ||
985 | * Tag which caused an error. | ||
986 | * | ||
987 | * @var string | null | ||
988 | */ | ||
989 | protected $tag = null; | ||
990 | |||
991 | /** | ||
992 | * Returns line in template file where error has occured | ||
993 | * or null if line is not defined. | ||
994 | * | ||
995 | * @return int | null | ||
996 | */ | ||
997 | public function getTemplateLine() | ||
998 | { | ||
999 | return $this->templateLine; | ||
1000 | } | ||
1001 | |||
1002 | /** | ||
1003 | * Sets line in template file where error has occured. | ||
1004 | * | ||
1005 | * @param int $templateLine | ||
1006 | * @return RainTpl_SyntaxException | ||
1007 | */ | ||
1008 | public function setTemplateLine($templateLine) | ||
1009 | { | ||
1010 | $this->templateLine = (int) $templateLine; | ||
1011 | return $this; | ||
1012 | } | ||
1013 | |||
1014 | /** | ||
1015 | * Returns tag which caused an error. | ||
1016 | * | ||
1017 | * @return string | ||
1018 | */ | ||
1019 | public function getTag() | ||
1020 | { | ||
1021 | return $this->tag; | ||
1022 | } | ||
1023 | |||
1024 | /** | ||
1025 | * Sets tag which caused an error. | ||
1026 | * | ||
1027 | * @param string $tag | ||
1028 | * @return RainTpl_SyntaxException | ||
1029 | */ | ||
1030 | public function setTag($tag) | ||
1031 | { | ||
1032 | $this->tag = (string) $tag; | ||
1033 | return $this; | ||
1034 | } | ||
1035 | } | ||
1036 | |||
1037 | // -- end | ||