XTemplate::assign()   A
last analyzed

Complexity

Conditions 6
Paths 7

Size

Total Lines 23
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
c 0
b 0
f 0
dl 0
loc 23
rs 9.2222
cc 6
nc 7
nop 3
1
<?php
2
3
// When developing uncomment the line below, re-comment before making public
4
//error_reporting(E_ALL);
5
6
/**
7
 * XTemplate PHP templating engine
8
 *
9
 * @package XTemplate
10
 * @author Barnabas Debreceni [[email protected]]
11
 * @copyright Barnabas Debreceni 2000-2001
12
 * @author Jeremy Coates [[email protected]]
13
 * @copyright Jeremy Coates 2002-2007
14
 * @see license.txt LGPL / BSD license
15
 * @since PHP 5
16
 * @link $HeadURL: https://xtpl.svn.sourceforge.net/svnroot/xtpl/trunk/xtemplate.class.php $
17
 * @version $Id: xtemplate.class.php 21 2007-05-29 18:01:15Z cocomp $
18
 *
19
 *
20
 * XTemplate class - http://www.phpxtemplate.org/ (x)html / xml generation with templates - fast & easy
21
 * Latest stable & Subversion versions available @ http://sourceforge.net/projects/xtpl/
22
 * License: LGPL / BSD - see license.txt
23
 * Changelog: see changelog.txt
24
 */
25
class XTemplate {
26
27
	/**
28
	 * Properties
29
	 */
30
31
	/**
32
	 * Raw contents of the template file
33
	 *
34
	 * @access public
35
	 * @var string
36
	 */
37
	public $filecontents = '';
38
39
	/**
40
	 * Unparsed blocks
41
	 *
42
	 * @access public
43
	 * @var array
44
	 */
45
	public $blocks = array();
46
47
	/**
48
	 * Parsed blocks
49
	 *
50
	 * @var unknown_type
51
	 */
52
	public $parsed_blocks = array();
53
54
	/**
55
	 * Preparsed blocks (for file includes)
56
	 *
57
	 * @access public
58
	 * @var array
59
	 */
60
	public $preparsed_blocks = array();
61
62
	/**
63
	 * Block parsing order for recursive parsing
64
	 * (Sometimes reverse :)
65
	 *
66
	 * @access public
67
	 * @var array
68
	 */
69
	public $block_parse_order = array();
70
71
	/**
72
	 * Store sub-block names
73
	 * (For fast resetting)
74
	 *
75
	 * @access public
76
	 * @var array
77
	 */
78
	public $sub_blocks = array();
79
80
	/**
81
	 * Variables array
82
	 *
83
	 * @access public
84
	 * @var array
85
	 */
86
	public $vars = array();
87
88
	/**
89
	 * File variables array
90
	 *
91
	 * @access public
92
	 * @var array
93
	 */
94
	public $filevars = array();
95
96
	/**
97
	 * Filevars' parent block
98
	 *
99
	 * @access public
100
	 * @var array
101
	 */
102
	public $filevar_parent = array();
103
104
	/**
105
	 * File caching during duration of script
106
	 * e.g. files only cached to speed {FILE "filename"} repeats
107
	 *
108
	 * @access public
109
	 * @var array
110
	 */
111
	public $filecache = array();
112
113
	/**
114
	 * Location of template files
115
	 *
116
	 * @access public
117
	 * @var string
118
	 */
119
	public $tpldir = '';
120
121
	/**
122
	 * Filenames lookup table
123
	 *
124
	 * @access public
125
	 * @var null
126
	 */
127
	public $files = null;
128
129
	/**
130
	 * Template filename
131
	 *
132
	 * @access public
133
	 * @var string
134
	 */
135
	public $filename = '';
136
137
	// moved to setup method so uses the tag_start & end_delims
138
	/**
139
	 * RegEx for file includes
140
	 *
141
	 * "/\{FILE\s*\"([^\"]+)\"\s*\}/m";
142
	 *
143
	 * @access public
144
	 * @var string
145
	 */
146
	public $file_delim = '';
147
148
	/**
149
	 * RegEx for file include variable
150
	 *
151
	 * "/\{FILE\s*\{([A-Za-z0-9\._]+?)\}\s*\}/m";
152
	 *
153
	 * @access public
154
	 * @var string
155
	 */
156
	public $filevar_delim = '';
157
158
	/**
159
	 * RegEx for file includes with newlines
160
	 *
161
	 * "/^\s*\{FILE\s*\{([A-Za-z0-9\._]+?)\}\s*\}\s*\n/m";
162
	 *
163
	 * @access public
164
	 * @var string
165
	 */
166
	public $filevar_delim_nl = '';
167
168
	/**
169
	 * Template block start delimiter
170
	 *
171
	 * @access public
172
	 * @var string
173
	 */
174
	public $block_start_delim = '<!-- ';
175
176
	/**
177
	 * Template block end delimiter
178
	 *
179
	 * @access public
180
	 * @var string
181
	 */
182
	public $block_end_delim = '-->';
183
184
	/**
185
	 * Template block start word
186
	 *
187
	 * @access public
188
	 * @var string
189
	 */
190
	public $block_start_word = 'BEGIN:';
191
192
	/**
193
	 * Template block end word
194
	 *
195
	 * The last 3 properties and this make the delimiters look like:
196
	 * @example <!-- BEGIN: block_name -->
197
	 * if you use the default syntax.
198
	 *
199
	 * @access public
200
	 * @var string
201
	 */
202
	public $block_end_word = 'END:';
203
204
	/**
205
	 * Template tag start delimiter
206
	 *
207
	 * This makes the delimiters look like:
208
	 * @example {tagname}
209
	 * if you use the default syntax.
210
	 *
211
	 * @access public
212
	 * @var string
213
	 */
214
	public $tag_start_delim = '{';
215
216
	/**
217
	 * Template tag end delimiter
218
	 *
219
	 * This makes the delimiters look like:
220
	 * @example {tagname}
221
	 * if you use the default syntax.
222
	 *
223
	 * @access public
224
	 * @var string
225
	 */
226
	public $tag_end_delim = '}';
227
	/* this makes the delimiters look like: {tagname} if you use my syntax. */
228
229
	/**
230
	 * Regular expression element for comments within tags and blocks
231
	 *
232
	 * @example {tagname#My Comment}
233
	 * @example {tagname #My Comment}
234
	 * @example <!-- BEGIN: blockname#My Comment -->
235
	 * @example <!-- BEGIN: blockname #My Comment -->
236
	 *
237
	 * @access public
238
	 * @var string
239
	 */
240
	public $comment_preg = '( ?#.*?)?';
241
242
	/**
243
	 * Default main template block name
244
	 *
245
	 * @access public
246
	 * @var string
247
	 */
248
	public $mainblock = 'main';
249
250
	/**
251
	 * Script output type
252
	 *
253
	 * @access public
254
	 * @var string
255
	 */
256
	public $output_type = 'HTML';
257
258
	/**
259
	 * Debug mode
260
	 *
261
	 * @access public
262
	 * @var boolean
263
	 */
264
	public $debug = false;
265
266
	/**
267
	 * Null string for unassigned vars
268
	 *
269
	 * @access protected
270
	 * @var array
271
	 */
272
	protected $_null_string = array('' => '');
273
274
	/**
275
	 * Null string for unassigned blocks
276
	 *
277
	 * @access protected
278
	 * @var array
279
	 */
280
	protected $_null_block = array('' => '');
281
282
	/**
283
	 * Errors
284
	 *
285
	 * @access protected
286
	 * @var string
287
	 */
288
	protected $_error = '';
289
290
	/**
291
	 * Auto-reset sub blocks
292
	 *
293
	 * @access protected
294
	 * @var boolean
295
	 */
296
	protected $_autoreset = true;
297
298
	/**
299
	 * Set to FALSE to generate errors if a non-existant blocks is referenced
300
	 *
301
	 * @author NW
302
	 * @since 2002/10/17
303
	 * @access protected
304
	 * @var boolean
305
	 */
306
	protected $_ignore_missing_blocks = true;
307
308
	/**
309
     * PHP 5 Constructor - Instantiate the object
310
     *
311
     * @param string $file Template file to work on
312
     * @param string/array $tpldir Location of template files (useful for keeping files outside web server root)
313
     * @param array $files Filenames lookup
314
     * @param string $mainblock Name of main block in the template
315
     * @param boolean $autosetup If true, run setup() as part of constuctor
316
     * @return XTemplate
317
     */
318
	public function __construct($file, $tpldir = '', $files = null, $mainblock = 'main', $autosetup = true) {
319
320
		$this->restart($file, $tpldir, $files, $mainblock, $autosetup, $this->tag_start_delim, $this->tag_end_delim);
321
	}
322
323
	/**
324
     * PHP 4 Constructor - Instantiate the object
325
     *
326
     * @deprecated Use PHP 5 constructor instead
327
     * @param string $file Template file to work on
328
     * @param string/array $tpldir Location of template files (useful for keeping files outside web server root)
329
     * @param array $files Filenames lookup
330
     * @param string $mainblock Name of main block in the template
331
     * @param boolean $autosetup If true, run setup() as part of constuctor
332
     * @return XTemplate
333
     */
334
	public function XTemplate ($file, $tpldir = '', $files = null, $mainblock = 'main', $autosetup = true) {
335
336
		assert('Deprecated - use PHP 5 constructor');
337
	}
338
339
340
	/***************************************************************************/
341
	/***[ public stuff ]********************************************************/
342
	/***************************************************************************/
343
344
	/**
345
	 * Restart the class - allows one instantiation with several files processed by restarting
346
	 * e.g. $xtpl = new XTemplate('file1.xtpl');
347
	 * $xtpl->parse('main');
348
	 * $xtpl->out('main');
349
	 * $xtpl->restart('file2.xtpl');
350
	 * $xtpl->parse('main');
351
	 * $xtpl->out('main');
352
	 * (Added in response to sf:641407 feature request)
353
	 *
354
	 * @param string $file Template file to work on
355
	 * @param string/array $tpldir Location of template files
356
	 * @param array $files Filenames lookup
357
	 * @param string $mainblock Name of main block in the template
358
	 * @param boolean $autosetup If true, run setup() as part of restarting
359
	 * @param string $tag_start {
360
	 * @param string $tag_end }
361
	 */
362
	public function restart ($file, $tpldir = '', $files = null, $mainblock = 'main', $autosetup = true, $tag_start = '{', $tag_end = '}') {
363
364
		$this->filename = $file;
365
366
		// From SF Feature request 1202027
367
		// Kenneth Kalmer
368
		$this->tpldir = $tpldir;
369
		if (defined('XTPL_DIR') && empty($this->tpldir)) {
370
			$this->tpldir = XTPL_DIR;
0 ignored issues
show
Bug introduced by
The constant XTPL_DIR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
371
		}
372
373
		if (is_array($files)) {
374
			$this->files = $files;
0 ignored issues
show
Documentation Bug introduced by
It seems like $files of type array is incompatible with the declared type null of property $files.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
375
		}
376
377
		$this->mainblock = $mainblock;
378
379
		$this->tag_start_delim = $tag_start;
380
		$this->tag_end_delim = $tag_end;
381
382
		// Start with fresh file contents
383
		$this->filecontents = '';
384
385
		// Reset the template arrays
386
		$this->blocks = array();
387
		$this->parsed_blocks = array();
388
		$this->preparsed_blocks = array();
389
		$this->block_parse_order = array();
390
		$this->sub_blocks = array();
391
		$this->vars = array();
392
		$this->filevars = array();
393
		$this->filevar_parent = array();
394
		$this->filecache = array();
395
396
		if ($autosetup) {
397
			$this->setup();
398
		}
399
	}
400
401
	/**
402
     * setup - the elements that were previously in the constructor
403
     *
404
     * @access public
405
     * @param boolean $add_outer If true is passed when called, it adds an outer main block to the file
406
     */
407
	public function setup ($add_outer = false) {
408
409
		$this->tag_start_delim = preg_quote($this->tag_start_delim);
410
		$this->tag_end_delim = preg_quote($this->tag_end_delim);
411
412
		// Setup the file delimiters
413
414
		// regexp for file includes
415
		$this->file_delim = "/" . $this->tag_start_delim . "FILE\s*\"([^\"]+)\"" . $this->comment_preg . $this->tag_end_delim . "/m";
416
417
		// regexp for file includes
418
		$this->filevar_delim = "/" . $this->tag_start_delim . "FILE\s*" . $this->tag_start_delim . "([A-Za-z0-9\._]+?)" . $this->comment_preg . $this->tag_end_delim . $this->comment_preg . $this->tag_end_delim . "/m";
419
420
		// regexp for file includes w/ newlines
421
		$this->filevar_delim_nl = "/^\s*" . $this->tag_start_delim . "FILE\s*" . $this->tag_start_delim . "([A-Za-z0-9\._]+?)" . $this->comment_preg . $this->tag_end_delim . $this->comment_preg . $this->tag_end_delim . "\s*\n/m";
422
423
		if (empty($this->filecontents)) {
424
			// read in template file
425
			$this->filecontents = $this->_r_getfile($this->filename);
426
		}
427
428
		if ($add_outer) {
429
			$this->_add_outer_block();
430
		}
431
432
		// preprocess some stuff
433
		$this->blocks = $this->_maketree($this->filecontents, '');
434
		$this->filevar_parent = $this->_store_filevar_parents($this->blocks);
435
		$this->scan_globals();
436
	}
437
438
	/**
439
     * assign a variable
440
     *
441
     * @example Simplest case:
442
     * @example $xtpl->assign('name', 'value');
443
     * @example {name} in template
444
     *
445
     * @example Array assign:
446
     * @example $xtpl->assign(array('name' => 'value', 'name2' => 'value2'));
447
     * @example {name} {name2} in template
448
     *
449
     * @example Value as array assign:
450
     * @example $xtpl->assign('name', array('key' => 'value', 'key2' => 'value2'));
451
     * @example {name.key} {name.key2} in template
452
     *
453
     * @example Reset array:
454
     * @example $xtpl->assign('name', array('key' => 'value', 'key2' => 'value2'));
455
     * @example // Other code then:
456
     * @example $xtpl->assign('name', array('key3' => 'value3'), false);
457
     * @example {name.key} {name.key2} {name.key3} in template
458
     *
459
     * @access public
460
     * @param string $name Variable to assign $val to
461
     * @param string / array $val Value to assign to $name
462
	 * @param boolean $reset_array Reset the variable array if $val is an array
463
     */
464
	public function assign ($name, $val = '', $reset_array = true) {
465
466
		if (is_array($name)) {
467
468
			foreach ($name as $k => $v) {
469
470
				$this->vars[$k] = $v;
471
			}
472
		} elseif (is_array($val)) {
473
474
			// Clear the existing values
475
    		if ($reset_array) {
476
    			$this->vars[$name] = array();
477
    		}
478
479
        	foreach ($val as $k => $v) {
480
481
        		$this->vars[$name][$k] = $v;
482
        	}
483
484
		} else {
485
486
			$this->vars[$name] = $val;
487
		}
488
	}
489
490
	/**
491
     * assign a file variable
492
     *
493
     * @access public
494
     * @param string $name Variable to assign $val to
495
     * @param string / array $val Values to assign to $name
496
     */
497
	public function assign_file ($name, $val = '') {
498
499
		if (is_array($name)) {
500
501
			foreach ($name as $k => $v) {
502
503
				$this->_assign_file_sub($k, $v);
504
			}
505
		} else {
506
507
			$this->_assign_file_sub($name, $val);
508
		}
509
	}
510
511
	/**
512
     * parse a block
513
     *
514
     * @access public
515
     * @param string $bname Block name to parse
516
     */
517
	public function parse ($bname) {
518
519
		if (isset($this->preparsed_blocks[$bname])) {
520
521
			$copy = $this->preparsed_blocks[$bname];
522
523
		} elseif (isset($this->blocks[$bname])) {
524
525
			$copy = $this->blocks[$bname];
526
527
		} elseif ($this->_ignore_missing_blocks) {
528
			// ------------------------------------------------------
529
			// NW : 17 Oct 2002. Added default of ignore_missing_blocks
530
			//      to allow for generalised processing where some
531
			//      blocks may be removed from the HTML without the
532
			//      processing code needing to be altered.
533
			// ------------------------------------------------------
534
			// JRC: 3/1/2003 added set error to ignore missing functionality
535
			$this->_set_error("parse: blockname [$bname] does not exist");
536
			return;
537
538
		} else {
539
540
			$this->_set_error("parse: blockname [$bname] does not exist");
541
		}
542
543
		/* from there we should have no more {FILE } directives */
544
		if (!isset($copy)) {
545
			die('Block: ' . $bname);
546
		}
547
548
		$copy = preg_replace($this->filevar_delim_nl, '', $copy);
549
550
		$var_array = array();
551
552
		/* find & replace variables+blocks */
553
		preg_match_all("|" . $this->tag_start_delim . "([A-Za-z0-9\._]+?" . $this->comment_preg . ")" . $this->tag_end_delim. "|", $copy, $var_array);
554
555
		$var_array = $var_array[1];
556
557
		foreach ($var_array as $k => $v) {
558
559
			// Are there any comments in the tags {tag#a comment for documenting the template}
560
			$any_comments = explode('#', $v);
561
			$v = rtrim($any_comments[0]);
562
563
			if (sizeof($any_comments) > 1) {
564
565
				$comments = $any_comments[1];
566
			} else {
567
568
				$comments = '';
569
			}
570
571
			$sub = explode('.', $v);
572
573
			if ($sub[0] == '_BLOCK_') {
574
575
				unset($sub[0]);
576
577
				$bname2 = implode('.', $sub);
578
579
				// trinary operator eliminates assign error in E_ALL reporting
580
				$var = isset($this->parsed_blocks[$bname2]) ? $this->parsed_blocks[$bname2] : null;
581
				$nul = (!isset($this->_null_block[$bname2])) ? $this->_null_block[''] : $this->_null_block[$bname2];
582
583
				if ($var === '') {
584
585
					if ($nul == '') {
586
						// -----------------------------------------------------------
587
						// Removed requirement for blocks to be at the start of string
588
						// -----------------------------------------------------------
589
						//                      $copy=preg_replace("/^\s*\{".$v."\}\s*\n*/m","",$copy);
590
						// Now blocks don't need to be at the beginning of a line,
591
						//$copy=preg_replace("/\s*" . $this->tag_start_delim . $v . $this->tag_end_delim . "\s*\n*/m","",$copy);
592
						$copy = preg_replace("|" . $this->tag_start_delim . $v . $this->tag_end_delim . "|m", '', $copy);
593
594
					} else {
595
596
						$copy = preg_replace("|" . $this->tag_start_delim . $v . $this->tag_end_delim . "|m", "$nul", $copy);
597
					}
598
				} else {
599
600
					//$var = trim($var);
601
					switch (true) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/^\n/', $var) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/\n$/', $var) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
602
						case preg_match('/^\n/', $var) && preg_match('/\n$/', $var):
603
							$var = substr($var, 1, -1);
604
							break;
605
606
						case preg_match('/^\n/', $var):
607
							$var = substr($var, 1);
608
							break;
609
610
						case preg_match('/\n$/', $var):
611
							$var = substr($var, 0, -1);
612
							break;
613
					}
614
615
					// SF Bug no. 810773 - thanks anonymous
616
					$var = str_replace('\\', '\\\\', $var);
617
					// Ensure dollars in strings are not evaluated reported by SadGeezer 31/3/04
618
					$var = str_replace('$', '\\$', $var);
619
					// Replaced str_replaces with preg_quote
620
					//$var = preg_quote($var);
621
					$var = str_replace('\\|', '|', $var);
622
					$copy = preg_replace("|" . $this->tag_start_delim . $v . $this->tag_end_delim . "|m", "$var", $copy);
623
624
					if (preg_match('/^\n/', $copy) && preg_match('/\n$/', $copy)) {
625
						$copy = substr($copy, 1, -1);
626
					}
627
				}
628
			} else {
629
630
				$var = $this->vars;
631
632
				foreach ($sub as $v1) {
633
634
					// NW 4 Oct 2002 - Added isset and is_array check to avoid NOTICE messages
635
					// JC 17 Oct 2002 - Changed EMPTY to stlen=0
636
					//                if (empty($var[$v1])) { // this line would think that zeros(0) were empty - which is not true
637
					if (!isset($var[$v1]) || (!is_array($var[$v1]) && strlen($var[$v1]) == 0)) {
638
639
						// Check for constant, when variable not assigned
640
						if (defined($v1)) {
641
642
							$var[$v1] = constant($v1);
643
644
						} else {
645
646
							$var[$v1] = null;
647
						}
648
					}
649
650
					$var = $var[$v1];
651
				}
652
653
				$nul = (!isset($this->_null_string[$v])) ? ($this->_null_string[""]) : ($this->_null_string[$v]);
654
				$var = (!isset($var)) ? $nul : $var;
655
656
				if ($var === '') {
657
					// -----------------------------------------------------------
658
					// Removed requriement for blocks to be at the start of string
659
					// -----------------------------------------------------------
660
					//                    $copy=preg_replace("|^\s*\{".$v." ?#?".$comments."\}\s*\n|m","",$copy);
661
					$copy = preg_replace("|" . $this->tag_start_delim . $v . "( ?#" . $comments . ")?" . $this->tag_end_delim . "|m", '', $copy);
662
				}
663
664
				$var = trim($var);
665
				// SF Bug no. 810773 - thanks anonymous
666
				$var = str_replace('\\', '\\\\', $var);
667
				// Ensure dollars in strings are not evaluated reported by SadGeezer 31/3/04
668
				$var = str_replace('$', '\\$', $var);
669
				// Replace str_replaces with preg_quote
670
				//$var = preg_quote($var);
671
				$var = str_replace('\\|', '|', $var);
672
				$copy = preg_replace("|" . $this->tag_start_delim . $v . "( ?#" . $comments . ")?" . $this->tag_end_delim . "|m", "$var", $copy);
673
674
				if (preg_match('/^\n/', $copy) && preg_match('/\n$/', $copy)) {
675
					$copy = substr($copy, 1);
676
				}
677
			}
678
		}
679
680
		if (isset($this->parsed_blocks[$bname])) {
681
			$this->parsed_blocks[$bname] .= $copy;
682
		} else {
683
			$this->parsed_blocks[$bname] = $copy;
684
		}
685
686
		/* reset sub-blocks */
687
		if ($this->_autoreset && (!empty($this->sub_blocks[$bname]))) {
688
689
			reset($this->sub_blocks[$bname]);
690
691
			foreach ($this->sub_blocks[$bname] as $k => $v) {
692
				$this->reset($v);
693
			}
694
		}
695
	}
696
697
	/**
698
     * returns the parsed text for a block, including all sub-blocks.
699
     *
700
     * @access public
701
     * @param string $bname Block name to parse
702
     */
703
	public function rparse ($bname) {
704
705
		if (!empty($this->sub_blocks[$bname])) {
706
707
			reset($this->sub_blocks[$bname]);
708
709
			foreach ($this->sub_blocks[$bname] as $k => $v) {
710
711
				if (!empty($v)) {
712
					$this->rparse($v);
713
				}
714
			}
715
		}
716
717
		$this->parse($bname);
718
	}
719
720
	/**
721
     * inserts a loop ( call assign & parse )
722
     *
723
     * @access public
724
     * @param string $bname Block name to assign
725
     * @param string $var Variable to assign values to
726
     * @param string / array $value Value to assign to $var
727
    */
728
	public function insert_loop ($bname, $var, $value = '') {
729
730
		$this->assign($var, $value);
731
		$this->parse($bname);
732
	}
733
734
	/**
735
     * parses a block for every set of data in the values array
736
     *
737
     * @access public
738
     * @param string $bname Block name to loop
739
     * @param string $var Variable to assign values to
740
     * @param array $values Values to assign to $var
741
    */
742
	public function array_loop ($bname, $var, &$values) {
743
744
		if (is_array($values)) {
0 ignored issues
show
introduced by
The condition is_array($values) is always true.
Loading history...
745
746
			foreach($values as $v) {
747
748
				$this->insert_loop($bname, $var, $v);
749
			}
750
		}
751
	}
752
753
	/**
754
     * returns the parsed text for a block
755
     *
756
     * @access public
757
     * @param string $bname Block name to return
758
     * @return string
759
     */
760
	public function text ($bname = '') {
761
762
		$text = '';
763
764
		if ($this->debug && $this->output_type == 'HTML') {
765
			// JC 20/11/02 echo the template filename if in development as
766
			// html comment
767
			$text .= '<!-- XTemplate: ' . realpath($this->filename) . " -->\n";
768
		}
769
770
		$bname = !empty($bname) ? $bname : $this->mainblock;
771
772
		$text .= isset($this->parsed_blocks[$bname]) ? $this->parsed_blocks[$bname] : $this->get_error();
773
774
		return $text;
775
	}
776
777
	/**
778
     * prints the parsed text
779
     *
780
     * @access public
781
     * @param string $bname Block name to echo out
782
     */
783
	public function out ($bname) {
784
785
		$out = $this->text($bname);
786
		//        $length=strlen($out);
787
		//header("Content-Length: ".$length); // TODO: Comment this back in later
788
789
		echo $out;
790
	}
791
792
	/**
793
     * prints the parsed text to a specified file
794
     *
795
     * @access public
796
     * @param string $bname Block name to write out
797
     * @param string $fname File name to write to
798
     */
799
	public function out_file ($bname, $fname) {
800
801
		if (!empty($bname) && !empty($fname) && is_writeable($fname)) {
802
803
			$fp = fopen($fname, 'w');
804
			fwrite($fp, $this->text($bname));
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

804
			fwrite(/** @scrutinizer ignore-type */ $fp, $this->text($bname));
Loading history...
805
			fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

805
			fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
806
		}
807
	}
808
809
	/**
810
     * resets the parsed text
811
     *
812
     * @access public
813
     * @param string $bname Block to reset
814
     */
815
	public function reset ($bname) {
816
817
		$this->parsed_blocks[$bname] = '';
818
	}
819
820
	/**
821
     * returns true if block was parsed, false if not
822
     *
823
     * @access public
824
     * @param string $bname Block name to test
825
     * @return boolean
826
     */
827
	public function parsed ($bname) {
828
829
		return (!empty($this->parsed_blocks[$bname]));
830
	}
831
832
	/**
833
     * sets the string to replace in case the var was not assigned
834
     *
835
     * @access public
836
     * @param string $str Display string for null block
837
     * @param string $varname Variable name to apply $str to
838
     */
839
	public function set_null_string($str, $varname = '') {
840
841
		$this->_null_string[$varname] = $str;
842
	}
843
844
	/**
845
	 * Backwards compatibility only
846
	 *
847
	 * @param string $str
848
	 * @param string $varname
849
	 * @deprecated Change to set_null_string to keep in with rest of naming convention
850
	 */
851
	public function SetNullString ($str, $varname = '') {
852
		$this->set_null_string($str, $varname);
853
	}
854
855
	/**
856
     * sets the string to replace in case the block was not parsed
857
     *
858
     * @access public
859
     * @param string $str Display string for null block
860
     * @param string $bname Block name to apply $str to
861
     */
862
	public function set_null_block ($str, $bname = '') {
863
864
		$this->_null_block[$bname] = $str;
865
	}
866
867
	/**
868
	 * Backwards compatibility only
869
	 *
870
	 * @param string $str
871
	 * @param string $bname
872
	 * @deprecated Change to set_null_block to keep in with rest of naming convention
873
	 */
874
	public function SetNullBlock ($str, $bname = '') {
875
		$this->set_null_block($str, $bname);
876
	}
877
878
	/**
879
     * sets AUTORESET to 1. (default is 1)
880
     * if set to 1, parse() automatically resets the parsed blocks' sub blocks
881
     * (for multiple level blocks)
882
     *
883
     * @access public
884
     */
885
	public function set_autoreset () {
886
887
		$this->_autoreset = true;
888
	}
889
890
	/**
891
     * sets AUTORESET to 0. (default is 1)
892
     * if set to 1, parse() automatically resets the parsed blocks' sub blocks
893
     * (for multiple level blocks)
894
     *
895
     * @access public
896
     */
897
	public function clear_autoreset () {
898
899
		$this->_autoreset = false;
900
	}
901
902
	/**
903
     * scans global variables and assigns to PHP array
904
     *
905
     * @access public
906
     */
907
	public function scan_globals () {
908
909
		reset($GLOBALS);
910
911
		foreach ($GLOBALS as $k => $v) {
912
			$GLOB[$k] = $v;
913
		}
914
915
		/**
916
		 * Access global variables as:
917
		 * @example {PHP._SERVER.HTTP_HOST}
918
		 * in your template!
919
		 */
920
		$this->assign('PHP', $GLOB);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $GLOB seems to be defined by a foreach iteration on line 911. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
921
	}
922
923
	/**
924
     * gets error condition / string
925
     *
926
     * @access public
927
     * @return boolean / string
928
     */
929
	public function get_error () {
930
931
		// JRC: 3/1/2003 Added ouptut wrapper and detection of output type for error message output
932
		$retval = false;
933
934
		if ($this->_error != '') {
935
936
			switch ($this->output_type) {
937
				case 'HTML':
938
				case 'html':
939
					$retval = '<b>[XTemplate]</b><ul>' . nl2br(str_replace('* ', '<li>', str_replace(" *\n", "</li>\n", $this->_error))) . '</ul>';
940
					break;
941
942
				default:
943
					$retval = '[XTemplate] ' . str_replace(' *\n', "\n", $this->_error);
944
					break;
945
			}
946
		}
947
948
		return $retval;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $retval also could return the type string which is incompatible with the documented return type boolean.
Loading history...
949
	}
950
951
	/***************************************************************************/
952
	/***[ private stuff ]*******************************************************/
953
	/***************************************************************************/
954
955
	/**
956
     * generates the array containing to-be-parsed stuff:
957
     * $blocks["main"],$blocks["main.table"],$blocks["main.table.row"], etc.
958
     * also builds the reverse parse order.
959
     *
960
     * @access public - aiming for private
961
     * @param string $con content to be processed
962
     * @param string $parentblock name of the parent block in the block hierarchy
963
     */
964
	public function _maketree ($con, $parentblock='') {
965
966
		$blocks = array();
967
968
		$con2 = explode($this->block_start_delim, $con);
969
970
		if (!empty($parentblock)) {
971
972
			$block_names = explode('.', $parentblock);
973
			$level = sizeof($block_names);
974
975
		} else {
976
977
			$block_names = array();
978
			$level = 0;
979
		}
980
981
		// JRC 06/04/2005 Added block comments (on BEGIN or END) <!-- BEGIN: block_name#Comments placed here -->
982
		//$patt = "($this->block_start_word|$this->block_end_word)\s*(\w+)\s*$this->block_end_delim(.*)";
983
		$patt = "(" . $this->block_start_word . "|" . $this->block_end_word . ")\s*(\w+)" . $this->comment_preg . "\s*" . $this->block_end_delim . "(.*)";
984
985
		foreach($con2 as $k => $v) {
986
987
			$res = array();
988
989
			if (preg_match_all("/$patt/ims", $v, $res, PREG_SET_ORDER)) {
990
				// $res[0][1] = BEGIN or END
991
				// $res[0][2] = block name
992
				// $res[0][3] = comment
993
				// $res[0][4] = kinda content
994
				$block_word	= $res[0][1];
995
				$block_name	= $res[0][2];
996
				$comment	= $res[0][3];
0 ignored issues
show
Unused Code introduced by
The assignment to $comment is dead and can be removed.
Loading history...
997
				$content	= $res[0][4];
998
999
				if (strtoupper($block_word) == $this->block_start_word) {
1000
1001
					$parent_name = implode('.', $block_names);
1002
1003
					// add one level - array("main","table","row")
1004
					$block_names[++$level] = $block_name;
1005
1006
					// make block name (main.table.row)
1007
					$cur_block_name=implode('.', $block_names);
1008
1009
					// build block parsing order (reverse)
1010
					$this->block_parse_order[] = $cur_block_name;
1011
1012
					//add contents. trinary operator eliminates assign error in E_ALL reporting
1013
					$blocks[$cur_block_name] = isset($blocks[$cur_block_name]) ? $blocks[$cur_block_name] . $content : $content;
1014
1015
					// add {_BLOCK_.blockname} string to parent block
1016
					$blocks[$parent_name] .= str_replace('\\', '', $this->tag_start_delim) . '_BLOCK_.' . $cur_block_name . str_replace('\\', '', $this->tag_end_delim);
1017
1018
					// store sub block names for autoresetting and recursive parsing
1019
					$this->sub_blocks[$parent_name][] = $cur_block_name;
1020
1021
					// store sub block names for autoresetting
1022
					$this->sub_blocks[$cur_block_name][] = '';
1023
1024
				} else if (strtoupper($block_word) == $this->block_end_word) {
1025
1026
					unset($block_names[$level--]);
1027
1028
					$parent_name = implode('.', $block_names);
1029
1030
					// add rest of block to parent block
1031
					$blocks[$parent_name] .= $content;
1032
				}
1033
			} else {
1034
1035
				// no block delimiters found
1036
				// Saves doing multiple implodes - less overhead
1037
				$tmp = implode('.', $block_names);
1038
1039
				if ($k) {
1040
					$blocks[$tmp] .= $this->block_start_delim;
1041
				}
1042
1043
				// trinary operator eliminates assign error in E_ALL reporting
1044
				$blocks[$tmp] = isset($blocks[$tmp]) ? $blocks[$tmp] . $v : $v;
1045
			}
1046
		}
1047
1048
		return $blocks;
1049
	}
1050
1051
	/**
1052
     * Sub processing for assign_file method
1053
     *
1054
     * @access private
1055
     * @param string $name
1056
     * @param string $val
1057
     */
1058
	private function _assign_file_sub ($name, $val) {
1059
1060
		if (isset($this->filevar_parent[$name])) {
1061
1062
			if ($val != '') {
1063
1064
				$val = $this->_r_getfile($val);
1065
1066
				foreach($this->filevar_parent[$name] as $parent) {
1067
1068
					if (isset($this->preparsed_blocks[$parent]) && !isset($this->filevars[$name])) {
1069
1070
						$copy = $this->preparsed_blocks[$parent];
1071
1072
					} elseif (isset($this->blocks[$parent])) {
1073
1074
						$copy = $this->blocks[$parent];
1075
					}
1076
1077
					$res = array();
1078
1079
					preg_match_all($this->filevar_delim, $copy, $res, PREG_SET_ORDER);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $copy does not seem to be defined for all execution paths leading up to this point.
Loading history...
1080
1081
					if (is_array($res) && isset($res[0])) {
1082
1083
						// Changed as per solution in SF bug ID #1261828
1084
						foreach ($res as $v) {
1085
1086
							// Changed as per solution in SF bug ID #1261828
1087
							if ($v[1] == $name) {
1088
1089
								// Changed as per solution in SF bug ID #1261828
1090
								$copy = preg_replace("/" . preg_quote($v[0]) . "/", "$val", $copy);
1091
								$this->preparsed_blocks = array_merge($this->preparsed_blocks, $this->_maketree($copy, $parent));
1092
								$this->filevar_parent = array_merge($this->filevar_parent, $this->_store_filevar_parents($this->preparsed_blocks));
1093
							}
1094
						}
1095
					}
1096
				}
1097
			}
1098
		}
1099
1100
		$this->filevars[$name] = $val;
1101
	}
1102
1103
	/**
1104
     * store container block's name for file variables
1105
     *
1106
     * @access public - aiming for private
1107
     * @param array $blocks
1108
     * @return array
1109
     */
1110
	public function _store_filevar_parents ($blocks){
1111
1112
		$parents = array();
1113
1114
		foreach ($blocks as $bname => $con) {
1115
1116
			$res = array();
1117
1118
			preg_match_all($this->filevar_delim, $con, $res);
1119
1120
			foreach ($res[1] as $k => $v) {
1121
1122
				$parents[$v][] = $bname;
1123
			}
1124
		}
1125
		return $parents;
1126
	}
1127
1128
	/**
1129
     * Set the error string
1130
     *
1131
     * @access private
1132
     * @param string $str
1133
     */
1134
	private function _set_error ($str)    {
1135
1136
		// JRC: 3/1/2003 Made to append the error messages
1137
		$this->_error .= '* ' . $str . " *\n";
1138
		// JRC: 3/1/2003 Removed trigger error, use this externally if you want it eg. trigger_error($xtpl->get_error())
1139
		//trigger_error($this->get_error());
1140
	}
1141
1142
	/**
1143
     * returns the contents of a file
1144
     *
1145
     * @access protected
1146
     * @param string $file
1147
     * @return string
1148
     */
1149
	protected function _getfile ($file) {
1150
1151
		if (!isset($file)) {
1152
			// JC 19/12/02 added $file to error message
1153
			$this->_set_error('!isset file name!' . $file);
1154
1155
			return '';
1156
		}
1157
1158
		// check if filename is mapped to other filename
1159
		if (isset($this->files)) {
1160
1161
			if (isset($this->files[$file])) {
1162
1163
				$file = $this->files[$file];
1164
			}
1165
		}
1166
1167
		// prepend template dir
1168
		if (!empty($this->tpldir)) {
1169
1170
			/**
1171
			 * Support hierarchy of file locations to search
1172
			 *
1173
			 * @example Supply array of filepaths when instantiating
1174
			 * 			First path supplied that has the named file is prioritised
1175
			 * 			$xtpl = new XTemplate('myfile.xtpl', array('.','/mypath', '/mypath2'));
1176
			 * @since 29/05/2007
1177
			 */
1178
			if (is_array($this->tpldir)) {
0 ignored issues
show
introduced by
The condition is_array($this->tpldir) is always false.
Loading history...
1179
1180
				foreach ($this->tpldir as $dir) {
1181
1182
					if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) {
1183
						$file = $dir . DIRECTORY_SEPARATOR . $file;
1184
						break;
1185
					}
1186
				}
1187
			} else {
1188
1189
				$file = $this->tpldir. DIRECTORY_SEPARATOR . $file;
1190
			}
1191
		}
1192
1193
		$file_text = '';
1194
1195
		if (isset($this->filecache[$file])) {
1196
1197
			$file_text .= $this->filecache[$file];
1198
1199
			if ($this->debug) {
1200
				$file_text = '<!-- XTemplate debug cached: ' . realpath($file) . ' -->' . "\n" . $file_text;
1201
			}
1202
1203
		} else {
1204
1205
			if (is_file($file) && is_readable($file)) {
1206
1207
				if (filesize($file)) {
1208
1209
					if (!($fh = fopen($file, 'r'))) {
1210
1211
						$this->_set_error('Cannot open file: ' . realpath($file));
1212
						return '';
1213
					}
1214
1215
					$file_text .= fread($fh,filesize($file));
1216
					fclose($fh);
1217
1218
				}
1219
1220
				if ($this->debug) {
1221
					$file_text = '<!-- XTemplate debug: ' . realpath($file) . ' -->' . "\n" . $file_text;
1222
				}
1223
1224
			} elseif (str_replace('.', '', phpversion()) >= '430' && $file_text = @file_get_contents($file, true)) {
1225
				// Enable use of include path by using file_get_contents
1226
				// Implemented at suggestion of SF Feature Request ID #1529478 michaelgroh
1227
				if ($file_text === false) {
1228
					$this->_set_error("[" . realpath($file) . "] ($file) does not exist");
1229
					$file_text = "<b>__XTemplate fatal error: file [$file] does not exist in the include path__</b>";
1230
				} elseif ($this->debug) {
1231
					$file_text = '<!-- XTemplate debug: ' . realpath($file) . ' (via include path) -->' . "\n" . $file_text;
1232
				}
1233
			} elseif (!is_file($file)) {
1234
1235
				// NW 17 Oct 2002 : Added realpath around the file name to identify where the code is searching.
1236
				$this->_set_error("[" . realpath($file) . "] ($file) does not exist");
1237
				$file_text .= "<b>__XTemplate fatal error: file [$file] does not exist__</b>";
1238
1239
			} elseif (!is_readable($file)) {
1240
1241
				$this->_set_error("[" . realpath($file) . "] ($file) is not readable");
1242
				$file_text .= "<b>__XTemplate fatal error: file [$file] is not readable__</b>";
1243
			}
1244
1245
			$this->filecache[$file] = $file_text;
1246
		}
1247
1248
		return $file_text;
1249
	}
1250
1251
	/**
1252
     * recursively gets the content of a file with {FILE "filename.tpl"} directives
1253
     *
1254
     * @access public - aiming for private
1255
     * @param string $file
1256
     * @return string
1257
     */
1258
	public function _r_getfile ($file) {
1259
1260
		$text = $this->_getfile($file);
1261
1262
		$res = array();
1263
1264
		while (preg_match($this->file_delim,$text,$res)) {
1265
1266
			$text2 = $this->_getfile($res[1]);
1267
			$text = preg_replace("'".preg_quote($res[0])."'",$text2,$text);
1268
		}
1269
1270
		return $text;
1271
	}
1272
1273
1274
	/**
1275
     * add an outer block delimiter set useful for rtfs etc - keeps them editable in word
1276
     *
1277
     * @access private
1278
     */
1279
	private function _add_outer_block () {
1280
1281
		$before = $this->block_start_delim . $this->block_start_word . ' ' . $this->mainblock . ' ' . $this->block_end_delim;
1282
		$after = $this->block_start_delim . $this->block_end_word . ' ' . $this->mainblock . ' ' . $this->block_end_delim;
1283
1284
		$this->filecontents = $before . "\n" . $this->filecontents . "\n" . $after;
1285
	}
1286
1287
	/**
1288
     * Debug function - var_dump wrapped in '<pre></pre>' tags
1289
     *
1290
     * @access private
1291
     * @param multiple var_dumps all the supplied arguments
1292
     */
1293
	private function _pre_var_dump ($args) {
0 ignored issues
show
Unused Code introduced by
The method _pre_var_dump() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
1294
1295
		if ($this->debug) {
1296
			echo '<pre>';
1297
			var_dump(func_get_args());
0 ignored issues
show
Security Debugging Code introduced by
var_dump(func_get_args()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
1298
			echo '</pre>';
1299
		}
1300
	}
1301
} /* end of XTemplate class. */
1302
1303
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...