Passed
Push — master ( 3e0ba1...df5150 )
by El
20:25 queued 10:25
created

lib/RainTPL.php (15 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * RainTPL
4
 *
5
 * Realized by Federico Ulfo & maintained by the Rain Team
6
 * Distributed under GNU/LGPL 3 License
7
 *
8
 * @version 2.7.2
9
 */
10
11
12
/**
13
 * RainTPL
14
 */
15
class RainTPL{
16
17
	// -------------------------
18
	// 	CONFIGURATION
19
	// -------------------------
20
21
		/**
22
		 * Template directory
23
		 *
24
		 * @var string
25
		 */
26
		static $tpl_dir = 'tpl/';
27
28
29
		/**
30
		 * Cache directory
31
		 *
32
		 * Is the directory where RainTPL will compile the template and save the cache
33
		 *
34
		 * @var string
35
		 */
36
		static $cache_dir = 'tmp/';
37
38
39
		/**
40
		 * Template base URL
41
		 *
42
		 * RainTPL will add this URL to the relative paths of element selected in $path_replace_list.
43
		 *
44
		 * @var string
45
		 */
46
		static $base_url = null;
47
48
49
		/**
50
		 * Template extension
51
		 *
52
		 * @var string
53
		 */
54
		static $tpl_ext = "html";
55
56
57
		/**
58
		 * Should the path be replaced
59
		 *
60
		 * Path replace is a cool features that replace all relative paths of images (&lt;img src="..."&gt;), stylesheet (&lt;link href="..."&gt;), script (&lt;script src="..."&gt;) and link (&lt;a href="..."&gt;)
61
		 * Set true to enable the path replace.
62
		 *
63
		 * @var boolean
64
		 */
65
		static $path_replace = true;
66
67
68
		/**
69
		 * You can set what the path_replace method will replace.
70
		 * Avaible options: a, img, link, script, input
71
		 *
72
		 * @var array
73
		 */
74
		static $path_replace_list = array( 'a', 'img', 'link', 'script', 'input' );
75
76
77
		/**
78
		 * You can define in the black list what string are disabled into the template tags
79
		 *
80
		 * @var array
81
		 */
82
		static $black_list = array( '\$this', 'raintpl::', 'self::', '_SESSION', '_SERVER', '_ENV',  'eval', 'exec', 'unlink', 'rmdir' );
83
84
85
		/**
86
		 * Check template
87
		 *
88
		 * true: checks template update time, if changed it compile them
89
		 * false: loads the compiled template. Set false if server doesn't have write permission for cache_directory.
90
		 *
91
		 * @var bool
92
		 */
93
		static $check_template_update = true;
94
95
96
		/**
97
		 * PHP tags <? ?>
98
		 *
99
		 * True: php tags are enabled into the template
100
		 * False: php tags are disabled into the template and rendered as html
101
		 *
102
		 * @var bool
103
		 */
104
		static $php_enabled = false;
105
106
107
		/**
108
		 * Debug mode flag
109
		 *
110
		 * True: debug mode is used, syntax errors are displayed directly in template. Execution of script is not terminated.
111
		 * False: exception is thrown on found error.
112
		 *
113
		 * @var bool
114
		 */
115
		static $debug = false;
116
117
	// -------------------------
118
119
120
	// -------------------------
121
	// 	RAINTPL VARIABLES
122
	// -------------------------
123
124
		/**
125
		 * Is the array where RainTPL keep the variables assigned
126
		 *
127
		 * @var array
128
		 */
129
		public $var = array();
130
131
		/**
132
		 * variables to keep the template directories and info
133
		 *
134
		 * @var array
135
		 */
136
		protected $tpl = array();		//
137
138
		/**
139
		 * static cache enabled / disabled
140
		 *
141
		 * @var bool
142
		 */
143
		protected $cache = false;
144
145
		/**
146
		 * identify only one cache
147
		 *
148
		 * @var string
149
		 */
150
		protected $cache_id = '';
151
152
		/**
153
		 * takes all the config to create the md5 of the file
154
		 *
155
		 * @var array the file
156
		 */
157
		protected static $config_name_sum = array();
158
159
	// -------------------------
160
161
162
163
	/**
164
	 * default cache expire time = hour
165
	 *
166
	 * @const int
167
	 */
168
	const CACHE_EXPIRE_TIME = 3600;
169
170
171
172
	/**
173
	 * Assign variable
174
	 * eg. 	$t->assign('name','mickey');
175
	 *
176
	 * @access public
177
	 * @param  mixed $variable_name Name of template variable or associative array name/value
178
	 * @param  mixed $value value assigned to this variable. Not set if variable_name is an associative array
179
	 */
180 4
	public function assign( $variable, $value = null ){
181 4
		if( is_array( $variable ) )
182 4
			$this->var += $variable;
183
		else
184 4
			$this->var[ $variable ] = $value;
185 4
	}
186
187
188
189
	/**
190
	 * Draw the template
191
	 * eg. 	$html = $tpl->draw( 'demo', TRUE ); // return template in string
192
	 * or 	$tpl->draw( $tpl_name ); // echo the template
193
	 *
194
	 * @access public
195
	 * @param  string $tpl_name  template to load
196
	 * @param  boolean $return_string  true=return a string, false=echo the template
197
	 * @return string
198
	 */
199 4
	public function draw( $tpl_name, $return_string = false ){
200
201
		try {
202
			// compile the template if necessary and set the template filepath
203 4
			$this->check_template( $tpl_name );
204 4
		} catch (RainTpl_Exception $e) {
205 1
			$output = $this->printDebug($e);
206
			die($output);
207
		}
208
209
		// Cache is off and, return_string is false
210
		// Rain just echo the template
211
212 4
		if( !$this->cache && !$return_string ){
213 4
			extract( $this->var );
214 4
			include $this->tpl['compiled_filename'];
215 4
			unset( $this->tpl );
216 4
		}
217
218
219
		// cache or return_string are enabled
220
		// rain get the output buffer to save the output in the cache or to return it as string
221
222
		else{
223
224
			//----------------------
225
			// get the output buffer
226
			//----------------------
227 2
				ob_start();
228 2
				extract( $this->var );
229 2
				include $this->tpl['compiled_filename'];
230 2
				$raintpl_contents = ob_get_clean();
231
			//----------------------
232
233
234
			// save the output in the cache
235 2
			if( $this->cache )
236 2
				file_put_contents( $this->tpl['cache_filename'], "<?php if(!class_exists('raintpl')){exit;}?>" . $raintpl_contents );
237
238
			// free memory
239 2
			unset( $this->tpl );
240
241
			// return or print the template
242 2
			if( $return_string ) return $raintpl_contents; else echo $raintpl_contents;
243
244
		}
245
246 4
	}
247
248
249
250
	/**
251
	 * If exists a valid cache for this template it returns the cache
252
	 *
253
	 * @access public
254
	 * @param  string $tpl_name Name of template (set the same of draw)
255
	 * @param  int $expiration_time Set after how many seconds the cache expire and must be regenerated
256
	 * @param  string $cache_id Suffix to be used when writing file to cache (optional)
257
	 * @return string it return the HTML or null if the cache must be recreated
258
	 */
259 2
	public function cache( $tpl_name, $expire_time = self::CACHE_EXPIRE_TIME, $cache_id = '' ){
260
261
		// set the cache_id
262 2
		$this->cache_id = $cache_id;
263
264 2
		if( !$this->check_template( $tpl_name ) && file_exists( $this->tpl['cache_filename'] ) && ( time() - filemtime( $this->tpl['cache_filename'] ) < $expire_time ) )
265 2
			return substr( file_get_contents( $this->tpl['cache_filename'] ), 43 );
266
		else{
267
			//delete the cache of the selected template
268 2
			if (file_exists($this->tpl['cache_filename']))
269 2
			unlink($this->tpl['cache_filename'] );
270 2
			$this->cache = true;
271
		}
272 2
	}
273
274
275
276
	/**
277
	 * Configure the settings of RainTPL
278
	 *
279
	 * @access public
280
	 * @static
281
	 * @param  array|string $setting array of settings or setting name
282
	 * @param  mixed $value content to set in the setting (optional)
283
	 */
284 2
	public static function configure( $setting, $value = null ){
285 2
		if( is_array( $setting ) )
286 2
			foreach( $setting as $key => $value )
287 2
				self::configure( $key, $value );
288 2
		else if( property_exists( __CLASS__, $setting ) ){
289 2
			self::$$setting = $value;
290 2
			self::$config_name_sum[ $setting ] = $value; // take trace of all config
291 2
		}
292 2
	}
293
294
295
296
	/**
297
	 * Check if has to compile the template
298
	 *
299
	 * @access protected
300
	 * @param  string $tpl_name template name to check
301
	 * @throws RainTpl_NotFoundException
302
	 * @return bool return true if the template has changed
303
	 */
304 4
	protected function check_template( $tpl_name ){
305
306 4
		if( !isset($this->tpl['checked']) ){
307
308 4
			$tpl_basename					   = basename( $tpl_name );														// template basename
309 4
			$tpl_basedir						= strpos($tpl_name,"/") ? dirname($tpl_name) . '/' : null;						// template basedirectory
310 4
			$tpl_dir							= PATH . self::$tpl_dir . $tpl_basedir;								// template directory
311 4
			$this->tpl['tpl_filename']		  = $tpl_dir . $tpl_basename . '.' . self::$tpl_ext;	// template filename
312 4
			$temp_compiled_filename			 = PATH . self::$cache_dir . $tpl_basename . "." . md5( $tpl_dir . serialize(self::$config_name_sum));
313 4
			$this->tpl['compiled_filename']	 = $temp_compiled_filename . '.rtpl.php';	// cache filename
314 4
			$this->tpl['cache_filename']		= $temp_compiled_filename . '.s_' . $this->cache_id . '.rtpl.php';	// static cache filename
315
316
			// if the template doesn't exsist throw an error
317 4
			if( self::$check_template_update && !file_exists( $this->tpl['tpl_filename'] ) ){
318 1
				$e = new RainTpl_NotFoundException( 'Template '. $tpl_basename .' not found!' );
319 1
				throw $e->setTemplateFile($this->tpl['tpl_filename']);
320
			}
321
322
			// file doesn't exsist, or the template was updated, Rain will compile the template
323 4
			if( !file_exists( $this->tpl['compiled_filename'] ) || ( self::$check_template_update && filemtime($this->tpl['compiled_filename']) < filemtime( $this->tpl['tpl_filename'] ) ) ){
324 2
				$this->compileFile( $tpl_basedir, $this->tpl['tpl_filename'], PATH . self::$cache_dir, $this->tpl['compiled_filename'] );
325 2
				return true;
326
			}
327 4
			$this->tpl['checked'] = true;
328 4
		}
329 4
	}
330
331
332
333
	/**
334
	 * execute stripslaches() on the xml block. Invoqued by preg_replace_callback function below
335
	 *
336
	 * @access protected
337
	 * @param string $capture
338
	 * @return string
339
	 */
340
	protected function xml_reSubstitution($capture) {
341
			return "<?php echo '<?xml ".stripslashes($capture[1])." ?>'; ?>";
342
	}
343
344
345
346
	/**
347
	 * Compile and write the compiled template file
348
	 *
349
	 * @access protected
350
	 * @param  string $tpl_basedir
351
	 * @param  string $tpl_filename
352
	 * @param  string $cache_dir
353
	 * @param  string $compiled_filename
354
	 * @throws RainTpl_Exception
355
	 * @return void
356
	 */
357 2
	protected function compileFile( $tpl_basedir, $tpl_filename, $cache_dir, $compiled_filename ){
358
359
		//read template file
360 2
		$this->tpl['source'] = $template_code = file_get_contents( $tpl_filename );
361
362
		//xml substitution
363 2
		$template_code = preg_replace( "/<\?xml(.*?)\?>/s", "##XML\\1XML##", $template_code );
364
365
		//disable php tag
366 2
		if( !self::$php_enabled )
367 2
			$template_code = str_replace( array("<?","?>"), array("&lt;?","?&gt;"), $template_code );
368
369
		//xml re-substitution
370 2
		$template_code = preg_replace_callback ( "/##XML(.*?)XML##/s", array($this, 'xml_reSubstitution'), $template_code );
371
372
		//compile template
373 2
		$template_compiled = "<?php if(!class_exists('raintpl')){exit;}?>" . $this->compileTemplate( $template_code, $tpl_basedir );
374
375
376
		// fix the php-eating-newline-after-closing-tag-problem
377 2
		$template_compiled = str_replace( "?>\n", "?>\n\n", $template_compiled );
378
379
		// create directories
380 2
		if( !is_dir( $cache_dir ) )
381 2
			mkdir( $cache_dir, 0755, true );
382
383 2
		if( !is_writable( $cache_dir ) )
384 2
			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/');
385
386
		//write compiled file
387 2
		file_put_contents( $compiled_filename, $template_compiled );
388 2
	}
389
390
391
392
	/**
393
	 * Compile template
394
	 *
395
	 * @access protected
396
	 * @param  string $template_code
397
	 * @param  string $tpl_basedir
398
	 * @return string
399
	 */
400 2
	protected function compileTemplate( $template_code, $tpl_basedir ){
401
402
		//tag list
403 2
		$tag_regexp = array( 'loop'		 => '(\{loop(?: name){0,1}="\${0,1}[^"]*"\})',
404 2
							 'loop_close'   => '(\{\/loop\})',
405 2
							 'if'		   => '(\{if(?: condition){0,1}="[^"]*"\})',
406 2
							 'elseif'	   => '(\{elseif(?: condition){0,1}="[^"]*"\})',
407 2
							 'else'		 => '(\{else\})',
408 2
							 'if_close'	 => '(\{\/if\})',
409 2
							 'function'	 => '(\{function="[^"]*"\})',
410 2
							 'noparse'	  => '(\{noparse\})',
411 2
							 'noparse_close'=> '(\{\/noparse\})',
412 2
							 'ignore'	   => '(\{ignore\}|\{\*)',
413 2
							 'ignore_close'	=> '(\{\/ignore\}|\*\})',
414 2
							 'include'	  => '(\{include="[^"]*"(?: cache="[^"]*")?\})',
415 2
							 'template_info'=> '(\{\$template_info\})',
416
							 'function'		=> '(\{function="(\w*?)(?:.*?)"\})'
417 2
							);
418
419 2
		$tag_regexp = "/" . join( "|", $tag_regexp ) . "/";
420
421
		//split the code with the tags regexp
422 2
		$template_code = preg_split ( $tag_regexp, $template_code, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
423
424
		//path replace (src of img, background and href of link)
425 2
		$template_code = $this->path_replace( $template_code, $tpl_basedir );
426
427
		//compile the code
428 2
		$compiled_code = $this->compileCode( $template_code );
429
430
		//return the compiled code
431 2
		return $compiled_code;
432
433
	}
434
435
436
437
	/**
438
	 * Compile the code
439
	 *
440
	 * @access protected
441
	 * @param  string $parsed_code
442
	 * @throws RainTpl_SyntaxException
443
	 * @return string
444
	 */
445 2
	protected function compileCode( $parsed_code ){
446
447
		//variables initialization
448 2
		$compiled_code = $open_if = $comment_is_open = $ignore_is_open = null;
449 2
		$loop_level = 0;
450
451
		//read all parsed code
452 2
		while( $html = array_shift( $parsed_code ) ){
453
454
			//close ignore tag
455 2
			if( !$comment_is_open && ( strpos( $html, '{/ignore}' ) !== FALSE || strpos( $html, '*}' ) !== FALSE ) )
456 2
				$ignore_is_open = false;
457
458
			//code between tag ignore id deleted
459 2
			elseif( $ignore_is_open ){
0 ignored issues
show
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
460
				//ignore the code
461
			}
462
463
			//close no parse tag
464 2
			elseif( strpos( $html, '{/noparse}' ) !== FALSE )
465
				$comment_is_open = false;
466
467
			//code between tag noparse is not compiled
468 2
			elseif( $comment_is_open )
469
				$compiled_code .= $html;
470
471
			//ignore
472 2
			elseif( strpos( $html, '{ignore}' ) !== FALSE || strpos( $html, '{*' ) !== FALSE )
473
				$ignore_is_open = true;
474
475
			//noparse
476 2
			elseif( strpos( $html, '{noparse}' ) !== FALSE )
477
				$comment_is_open = true;
478
479
			//include tag
480 2
			elseif( preg_match( '/\{include="([^"]*)"(?: cache="([^"]*)"){0,1}\}/', $html, $code ) ){
481
482
				//variables substitution
483
				$include_var = $this->var_replace( $code[ 1 ], $left_delimiter = null, $right_delimiter = null, $php_left_delimiter = '".' , $php_right_delimiter = '."', $loop_level );
484
485
				// if the cache is active
486
				if( isset($code[ 2 ]) ){
487
488
					//dynamic include
489
					$compiled_code .= '<?php $tpl = new '.get_class($this).';' .
490
								 'if( $cache = $tpl->cache( $template = basename("'.$include_var.'") ) )' .
491
								 '	echo $cache;' .
492
								 'else{' .
493
								 '	$tpl_dir_temp = self::$tpl_dir;' .
494
								 '	$tpl->assign( $this->var );' .
495
									( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ).
496
								 '	$tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'.
497
								 '} ?>';
498
				}
499
				else{
500
501
					//dynamic include
502
					$compiled_code .= '<?php $tpl = new '.get_class($this).';' .
503
									  '$tpl_dir_temp = self::$tpl_dir;' .
504
									  '$tpl->assign( $this->var );' .
505
									  ( !$loop_level ? null : '$tpl->assign( "key", $key'.$loop_level.' ); $tpl->assign( "value", $value'.$loop_level.' );' ).
506
									  '$tpl->draw( dirname("'.$include_var.'") . ( substr("'.$include_var.'",-1,1) != "/" ? "/" : "" ) . basename("'.$include_var.'") );'.
507
									  '?>';
508
509
510
				}
511
512
			}
513
514
			//loop
515 2
			elseif( preg_match( '/\{loop(?: name){0,1}="\${0,1}([^"]*)"\}/', $html, $code ) ){
516
517
				//increase the loop counter
518 2
				$loop_level++;
519
520
				//replace the variable in the loop
521 2
				$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 );
522
523
				//loop variables
524 2
				$counter = "\$counter$loop_level";	   // count iteration
525 2
				$key = "\$key$loop_level";			   // key
526 2
				$value = "\$value$loop_level";		   // value
527
528
				//loop code
529 2
				$compiled_code .=  "<?php $counter=-1; if( isset($var) && is_array($var) && sizeof($var) ) foreach( $var as $key => $value ){ $counter++; ?>";
530
531 2
			}
532
533
			//close loop tag
534 2
			elseif( strpos( $html, '{/loop}' ) !== FALSE ) {
535
536
				//iterator
537 2
				$counter = "\$counter$loop_level";
0 ignored issues
show
$counter is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
538
539
				//decrease the loop counter
540 2
				$loop_level--;
541
542
				//close loop code
543 2
				$compiled_code .=  "<?php } ?>";
544
545 2
			}
546
547
			//if
548 2 View Code Duplication
			elseif( preg_match( '/\{if(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
549
550
				//increase open if counter (for intendation)
551 2
				$open_if++;
552
553
				//tag
554 2
				$tag = $code[ 0 ];
555
556
				//condition attribute
557 2
				$condition = $code[ 1 ];
558
559
				// check if there's any function disabled by black_list
560 2
				$this->function_check( $tag );
561
562
				//variable substitution into condition (no delimiter into the condition)
563 2
				$parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
564
565
				//if code
566 2
				$compiled_code .=   "<?php if( $parsed_condition ){ ?>";
567
568 2
			}
569
570
			//elseif
571 2 View Code Duplication
			elseif( preg_match( '/\{elseif(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
572
573
				//tag
574
				$tag = $code[ 0 ];
0 ignored issues
show
$tag is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
575
576
				//condition attribute
577
				$condition = $code[ 1 ];
578
579
				//variable substitution into condition (no delimiter into the condition)
580
				$parsed_condition = $this->var_replace( $condition, $tag_left_delimiter = null, $tag_right_delimiter = null, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level );
581
582
				//elseif code
583
				$compiled_code .=   "<?php }elseif( $parsed_condition ){ ?>";
584
			}
585
586
			//else
587 2
			elseif( strpos( $html, '{else}' ) !== FALSE ) {
588
589
				//else code
590
				$compiled_code .=   '<?php }else{ ?>';
591
592
			}
593
594
			//close if tag
595 2
			elseif( strpos( $html, '{/if}' ) !== FALSE ) {
596
597
				//decrease if counter
598 2
				$open_if--;
599
600
				// close if code
601 2
				$compiled_code .=   '<?php } ?>';
602
603 2
			}
604
605
			//function
606 2
			elseif( preg_match( '/\{function="(\w*)(.*?)"\}/', $html, $code ) ){
607
608
				//tag
609 2
				$tag = $code[ 0 ];
610
611
				//function
612 2
				$function = $code[ 1 ];
613
614
				// check if there's any function disabled by black_list
615 2
				$this->function_check( $tag );
616
617 2
				if( empty( $code[ 2 ] ) )
618 2
					$parsed_function = $function . "()";
619
				else
620
					// parse the function
621 2
					$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 );
622
623
				//if code
624 2
				$compiled_code .=   "<?php echo $parsed_function; ?>";
625 2
			}
626
627
			// show all vars
628 2
			elseif ( strpos( $html, '{$template_info}' ) !== FALSE ) {
629
630
				//tag
631
				$tag  = '{$template_info}';
0 ignored issues
show
$tag is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
632
633
				//if code
634
				$compiled_code .=   '<?php echo "<pre>"; print_r( $this->var ); echo "</pre>"; ?>';
635
			}
636
637
638
			//all html code
639
			else{
640
641
				//variables substitution (es. {$title})
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
642 2
				$html = $this->var_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
643
				//const substitution (es. {#CONST#})
644 2
				$html = $this->const_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
645
				//functions substitution (es. {"string"|functions})
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
646 2
				$compiled_code .= $this->func_replace( $html, $left_delimiter = '\{', $right_delimiter = '\}', $php_left_delimiter = '<?php ', $php_right_delimiter = ';?>', $loop_level, $echo = true );
647
			}
648 2
		}
649
650 2
		if( $open_if > 0 ) {
651
			$e = new RainTpl_SyntaxException('Error! You need to close an {if} tag in ' . $this->tpl['tpl_filename'] . ' template');
652
			throw $e->setTemplateFile($this->tpl['tpl_filename']);
653
		}
654 2
		return $compiled_code;
655
	}
656
657
658
659
	/**
660
	 * Reduce a path
661
	 *
662
	 * eg. www/library/../filepath//file => www/filepath/file
663
	 *
664
	 * @param string $path
665
	 * @return string
666
	 */
667
	protected function reduce_path( $path ){
668
		$path = str_replace( "://", "@not_replace@", $path );
669
		$path = str_replace( "//", "/", $path );
670
		$path = str_replace( "@not_replace@", "://", $path );
671
		return preg_replace('/\w+\/\.\.\//', '', $path );
672
	}
673
674
675
676
	/**
677
	 * replace the path of image src, link href and a href
678
	 *
679
	 * url => template_dir/url
680
	 * url# => url
681
	 * http://url => http://url
682
	 *
683
	 * @access protected
684
	 * @param  string $html
685
	 * @param  string $tpl_basedir
686
	 * @return string html substitution
687
	 */
688 2
	protected function path_replace( $html, $tpl_basedir ){
689
690 2
		if( self::$path_replace ){
691
692
			$tpl_dir = self::$base_url . PATH . self::$tpl_dir . $tpl_basedir;
693
694
			// reduce the path
695
			$path = $this->reduce_path($tpl_dir);
696
697
			$exp = $sub = array();
698
699
			if( in_array( "img", self::$path_replace_list ) ){
700
				$exp = array( '/<img(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<img(.*?)src=(?:")([^"]+?)#(?:")/i', '/<img(.*?)src="(.*?)"/', '/<img(.*?)src=(?:\@)([^"]+?)(?:\@)/i' );
701
				$sub = array( '<img$1src=@$2://$3@', '<img$1src=@$2@', '<img$1src="' . $path . '$2"', '<img$1src="$2"' );
702
			}
703
704 View Code Duplication
			if( in_array( "script", self::$path_replace_list ) ){
705
				$exp = array_merge( $exp , array( '/<script(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<script(.*?)src=(?:")([^"]+?)#(?:")/i', '/<script(.*?)src="(.*?)"/', '/<script(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
706
				$sub = array_merge( $sub , array( '<script$1src=@$2://$3@', '<script$1src=@$2@', '<script$1src="' . $path . '$2"', '<script$1src="$2"' ) );
707
			}
708
709 View Code Duplication
			if( in_array( "link", self::$path_replace_list ) ){
710
				$exp = array_merge( $exp , array( '/<link(.*?)href=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<link(.*?)href=(?:")([^"]+?)#(?:")/i', '/<link(.*?)href="(.*?)"/', '/<link(.*?)href=(?:\@)([^"]+?)(?:\@)/i' ) );
711
				$sub = array_merge( $sub , array( '<link$1href=@$2://$3@', '<link$1href=@$2@' , '<link$1href="' . $path . '$2"', '<link$1href="$2"' ) );
712
			}
713
714
			if( in_array( "a", self::$path_replace_list ) ){
715
				$exp = array_merge( $exp , array( '/<a(.*?)href=(?:")(http\:\/\/|https\:\/\/|javascript:)([^"]+?)(?:")/i', '/<a(.*?)href="(.*?)"/', '/<a(.*?)href=(?:\@)([^"]+?)(?:\@)/i'  ) );
716
				$sub = array_merge( $sub , array( '<a$1href=@$2$3@', '<a$1href="' . self::$base_url . '$2"', '<a$1href="$2"' ) );
717
			}
718
719 View Code Duplication
			if( in_array( "input", self::$path_replace_list ) ){
720
				$exp = array_merge( $exp , array( '/<input(.*?)src=(?:")(http|https)\:\/\/([^"]+?)(?:")/i', '/<input(.*?)src=(?:")([^"]+?)#(?:")/i', '/<input(.*?)src="(.*?)"/', '/<input(.*?)src=(?:\@)([^"]+?)(?:\@)/i' ) );
721
				$sub = array_merge( $sub , array( '<input$1src=@$2://$3@', '<input$1src=@$2@', '<input$1src="' . $path . '$2"', '<input$1src="$2"' ) );
722
			}
723
724
			return preg_replace( $exp, $sub, $html );
725
726
		}
727
		else
728 2
			return $html;
729
730
	}
731
732
733
734
	/**
735
	 * replace constants
736
	 *
737
	 * @access public
738
	 * @param  string $html
739
	 * @param  string $tag_left_delimiter
740
	 * @param  string $tag_right_delimiter
741
	 * @param  string $php_left_delimiter (optional)
742
	 * @param  string $php_right_delimiter (optional)
743
	 * @param  string $loop_level (optional)
744
	 * @param  string $echo (optional)
745
	 * @return string
746
	 */
747 2
	public function const_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
0 ignored issues
show
The parameter $tag_left_delimiter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $tag_right_delimiter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $loop_level is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
748
		// const
749 2
		return preg_replace( '/\{\#(\w+)\#{0,1}\}/', $php_left_delimiter . ( $echo ? " echo " : null ) . '\\1' . $php_right_delimiter, $html );
750
	}
751
752
753
754
	/**
755
	 * replace functions/modifiers on constants and strings
756
	 *
757
	 * @access public
758
	 * @param  string $html
759
	 * @param  string $tag_left_delimiter
760
	 * @param  string $tag_right_delimiter
761
	 * @param  string $php_left_delimiter (optional)
762
	 * @param  string $php_right_delimiter (optional)
763
	 * @param  string $loop_level (optional)
764
	 * @param  string $echo (optional)
765
	 * @return string
766
	 */
767 2
	public function func_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
0 ignored issues
show
The parameter $tag_left_delimiter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $tag_right_delimiter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
768
769 2
		preg_match_all( '/' . '\{\#{0,1}(\"{0,1}.*?\"{0,1})(\|\w.*?)\#{0,1}\}' . '/', $html, $matches );
770
771 2
		for( $i=0, $n=count($matches[0]); $i<$n; $i++ ){
772
773
			//complete tag ex: {$news.title|substr:0,100}
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
774
			$tag = $matches[ 0 ][ $i ];
775
776
			//variable name ex: news.title
777
			$var = $matches[ 1 ][ $i ];
778
779
			//function and parameters associate to the variable ex: substr:0,100
780
			$extra_var = $matches[ 2 ][ $i ];
781
782
			// check if there's any function disabled by black_list
783
			$this->function_check( $tag );
784
785
			$extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
786
787
788
			// check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
789
			$is_init_variable = preg_match( "/^(\s*?)\=[^=](.*?)$/", $extra_var );
790
791
			//function associate to variable
792
			$function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
793
794
			//variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
795
			$temp = preg_split( "/\.|\[|\-\>/", $var );
796
797
			//variable name
798
			$var_name = $temp[ 0 ];
799
800
			//variable path
801
			$variable_path = substr( $var, strlen( $var_name ) );
802
803
			//parentesis transform [ e ] in [" e in "]
804
			$variable_path = str_replace( '[', '["', $variable_path );
805
			$variable_path = str_replace( ']', '"]', $variable_path );
806
807
			//transform .$variable in ["$variable"]
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
808
			$variable_path = preg_replace('/\.\$(\w+)/', '["$\\1"]', $variable_path );
809
810
			//transform [variable] in ["variable"]
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
811
			$variable_path = preg_replace('/\.(\w+)/', '["\\1"]', $variable_path );
812
813
			//if there's a function
814 View Code Duplication
			if( $function_var ){
815
816
				// check if there's a function or a static method and separate, function by parameters
817
				$function_var = str_replace("::", "@double_dot@", $function_var );
818
819
				// get the position of the first :
820
				if( $dot_position = strpos( $function_var, ":" ) ){
821
822
					// get the function and the parameters
823
					$function = substr( $function_var, 0, $dot_position );
824
					$params = substr( $function_var, $dot_position+1 );
825
826
				}
827
				else{
828
829
					//get the function
830
					$function = str_replace( "@double_dot@", "::", $function_var );
831
					$params = null;
832
833
				}
834
835
				// replace back the @double_dot@ with ::
836
				$function = str_replace( "@double_dot@", "::", $function );
837
				$params = str_replace( "@double_dot@", "::", $params );
838
839
840
			}
841
			else
842
				$function = $params = null;
843
844
			$php_var = $var_name . $variable_path;
845
846
			// compile the variable for php
847
			if( isset( $function ) ){
848
				if( $php_var )
849
					$php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
850
				else
851
					$php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $params ) )" : "$function()" ) . $php_right_delimiter;
852
			}
853 View Code Duplication
			else
854
				$php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
855
856
			$html = str_replace( $tag, $php_var, $html );
857
858
		}
859
860 2
		return $html;
861
862
	}
863
864
865
866
	/**
867
	 * replace variables
868
	 *
869
	 * @access public
870
	 * @param  string $html
871
	 * @param  string $tag_left_delimiter
872
	 * @param  string $tag_right_delimiter
873
	 * @param  string $php_left_delimiter (optional)
874
	 * @param  string $php_right_delimiter (optional)
875
	 * @param  string $loop_level (optional)
876
	 * @param  string $echo (optional)
877
	 * @return string
878
	 */
879 2
	public function var_replace( $html, $tag_left_delimiter, $tag_right_delimiter, $php_left_delimiter = null, $php_right_delimiter = null, $loop_level = null, $echo = null ){
880
881
		//all variables
882 2
		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 ) ){
883
884 2
			for( $parsed=array(), $i=0, $n=count($matches[0]); $i<$n; $i++ )
885 2
				$parsed[$matches[0][$i]] = array('var'=>$matches[1][$i],'extra_var'=>$matches[2][$i]);
886
887 2
			foreach( $parsed as $tag => $array ){
888
889
				//variable name ex: news.title
890 2
				$var = $array['var'];
891
892
				//function and parameters associate to the variable ex: substr:0,100
893 2
				$extra_var = $array['extra_var'];
894
895
				// check if there's any function disabled by black_list
896 2
				$this->function_check( $tag );
897
898 2
				$extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
899
900
				// check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
901 2
				$is_init_variable = preg_match( "/^[a-z_A-Z\.\[\](\-\>)]*=[^=]*$/", $extra_var );
902
903
				//function associate to variable
904 2
				$function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
905
906
				//variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
907 2
				$temp = preg_split( "/\.|\[|\-\>/", $var );
908
909
				//variable name
910 2
				$var_name = $temp[ 0 ];
911
912
				//variable path
913 2
				$variable_path = substr( $var, strlen( $var_name ) );
914
915
				//parentesis transform [ e ] in [" e in "]
916 2
				$variable_path = str_replace( '[', '["', $variable_path );
917 2
				$variable_path = str_replace( ']', '"]', $variable_path );
918
919
				//transform .$variable in ["$variable"] and .variable in ["variable"]
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
920 2
				$variable_path = preg_replace('/\.(\${0,1}\w+)/', '["\\1"]', $variable_path );
921
922
				// if is an assignment also assign the variable to $this->var['value']
923
				if( $is_init_variable )
924 2
					$extra_var = "=\$this->var['{$var_name}']{$variable_path}" . $extra_var;
925
926
927
928
				//if there's a function
929 2 View Code Duplication
				if( $function_var ){
930
931
					// check if there's a function or a static method and separate, function by parameters
932 2
					$function_var = str_replace("::", "@double_dot@", $function_var );
933
934
935
					// get the position of the first :
936 2
					if( $dot_position = strpos( $function_var, ":" ) ){
937
938
						// get the function and the parameters
939
						$function = substr( $function_var, 0, $dot_position );
940
						$params = substr( $function_var, $dot_position+1 );
941
942
					}
943
					else{
944
945
						//get the function
946 2
						$function = str_replace( "@double_dot@", "::", $function_var );
947 2
						$params = null;
948
949
					}
950
951
					// replace back the @double_dot@ with ::
952 2
					$function = str_replace( "@double_dot@", "::", $function );
953 2
					$params = str_replace( "@double_dot@", "::", $params );
954 2
				}
955
				else
956 2
					$function = $params = null;
957
958
				//if it is inside a loop
959 2
				if( $loop_level ){
960
					//verify the variable name
961 2
					if( $var_name == 'key' )
962 2
							$php_var = '$key' . $loop_level;
963 2
					elseif( $var_name == 'value' )
964 2
							$php_var = '$value' . $loop_level . $variable_path;
965 2
					elseif( $var_name == 'counter' )
966
							$php_var = '$counter' . $loop_level;
967
					else
968 2
							$php_var = '$' . $var_name . $variable_path;
969 2
				}else
970 2
					$php_var = '$' . $var_name . $variable_path;
971
972
				// compile the variable for php
973 2
				if( isset( $function ) )
974 2
					$php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
975 View Code Duplication
				else
976 2
					$php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
977
978 2
				$html = str_replace( $tag, $php_var, $html );
979
980
981 2
			}
982 2
		}
983
984 2
		return $html;
985
	}
986
987
988
989
	/**
990
	 * Check if function is in black list (sandbox)
991
	 *
992
	 * @access protected
993
	 * @param  string $code
994
	 * @throws RainTpl_SyntaxException
995
	 * @return void
996
	 */
997 2
	protected function function_check( $code ){
998
999 2
		$preg = '#(\W|\s)' . implode( '(\W|\s)|(\W|\s)', self::$black_list ) . '(\W|\s)#';
1000
1001
		// check if the function is in the black list (or not in white list)
1002 2
		if( count(self::$black_list) && preg_match( $preg, $code, $match ) ){
1003
1004
			// find the line of the error
1005
			$line = 0;
1006
			$rows=explode("\n",$this->tpl['source']);
1007
			while( !strpos($rows[$line],$code) )
1008
				$line++;
1009
1010
			// stop the execution of the script
1011
			$e = new RainTpl_SyntaxException('Unallowed syntax in ' . $this->tpl['tpl_filename'] . ' template');
1012
			throw $e->setTemplateFile($this->tpl['tpl_filename'])
1013
				->setTag($code)
1014
				->setTemplateLine($line);
1015
		}
1016
1017 2
	}
1018
1019
1020
1021
	/**
1022
	 * Prints debug info about exception or passes it further if debug is disabled.
1023
	 *
1024
	 * @access protected
1025
	 * @param  RainTpl_Exception $e
1026
	 * @return string
1027
	 */
1028 1
	protected function printDebug(RainTpl_Exception $e){
1029 1
		if (!self::$debug) {
1030 1
			throw $e;
1031
		}
1032
		$output = sprintf('<h2>Exception: %s</h2><h3>%s</h3><p>template: %s</p>',
1033
			get_class($e),
1034
			$e->getMessage(),
1035
			$e->getTemplateFile()
1036
		);
1037
		if ($e instanceof RainTpl_SyntaxException) {
1038
			if (null !== $e->getTemplateLine()) {
1039
				$output .= '<p>line: ' . $e->getTemplateLine() . '</p>';
1040
			}
1041
			if (null !== $e->getTag()) {
1042
				$output .= '<p>in tag: ' . htmlspecialchars($e->getTag()) . '</p>';
1043
			}
1044
			if (null !== $e->getTemplateLine() && null !== $e->getTag()) {
1045
				$rows=explode("\n",  htmlspecialchars($this->tpl['source']));
1046
				$rows[$e->getTemplateLine()] = '<font color=red>' . $rows[$e->getTemplateLine()] . '</font>';
1047
				$output .= '<h3>template code</h3>' . implode('<br />', $rows) . '</pre>';
1048
			}
1049
		}
1050
		$output .= sprintf('<h3>trace</h3><p>In %s on line %d</p><pre>%s</pre>',
1051
			$e->getFile(), $e->getLine(),
1052
			nl2br(htmlspecialchars($e->getTraceAsString()))
1053
		);
1054
		return $output;
1055
	}
1056
}
1057
1058
1059
1060
/**
1061
 * Basic Rain tpl exception.
1062
 */
1063
class RainTpl_Exception extends Exception{
1064
	/**
1065
	 * Path of template file with error.
1066
	 */
1067
	protected $templateFile = '';
1068
1069
	/**
1070
	 * Returns path of template file with error.
1071
	 *
1072
	 * @return string
1073
	 */
1074
	public function getTemplateFile()
1075
	{
1076
		return $this->templateFile;
1077
	}
1078
1079
	/**
1080
	 * Sets path of template file with error.
1081
	 *
1082
	 * @param string $templateFile
1083
	 * @return RainTpl_Exception
1084
	 */
1085 1
	public function setTemplateFile($templateFile)
1086
	{
1087 1
		$this->templateFile = (string) $templateFile;
1088 1
		return $this;
1089
	}
1090
}
1091
1092
1093
1094
/**
1095
 * Exception thrown when template file does not exists.
1096
 */
1097
class RainTpl_NotFoundException extends RainTpl_Exception{
1098
}
1099
1100
/**
1101
 * Exception thrown when syntax error occurs.
1102
 */
1103
class RainTpl_SyntaxException extends RainTpl_Exception{
1104
	/**
1105
	 * Line in template file where error has occured.
1106
	 *
1107
	 * @var int | null
1108
	 */
1109
	protected $templateLine = null;
1110
1111
	/**
1112
	 * Tag which caused an error.
1113
	 *
1114
	 * @var string | null
1115
	 */
1116
	protected $tag = null;
1117
1118
	/**
1119
	 * Returns line in template file where error has occured
1120
	 * or null if line is not defined.
1121
	 *
1122
	 * @return int | null
1123
	 */
1124
	public function getTemplateLine()
1125
	{
1126
		return $this->templateLine;
1127
	}
1128
1129
	/**
1130
	 * Sets  line in template file where error has occured.
1131
	 *
1132
	 * @param int $templateLine
1133
	 * @return RainTpl_SyntaxException
1134
	 */
1135
	public function setTemplateLine($templateLine)
1136
	{
1137
		$this->templateLine = (int) $templateLine;
1138
		return $this;
1139
	}
1140
1141
	/**
1142
	 * Returns tag which caused an error.
1143
	 *
1144
	 * @return string
1145
	 */
1146
	public function getTag()
1147
	{
1148
		return $this->tag;
1149
	}
1150
1151
	/**
1152
	 * Sets tag which caused an error.
1153
	 *
1154
	 * @param string $tag
1155
	 * @return RainTpl_SyntaxException
1156
	 */
1157
	public function setTag($tag)
1158
	{
1159
		$this->tag = (string) $tag;
1160
		return $this;
1161
	}
1162
}
1163
1164
// -- end
1165