Overview
Namespaces
Classes
- form_state
- sql_query
- sql_query_builder
- stash_bean
- stash_collection
- stash_framework
- stash_mysql
- stash_mysql_collection
- stash_mysql_framework
- XTemplate
- ztemplate
Functions
1: <?php
2: /**
3: * zTemplate
4: *
5: * This is a class which emulates XTemplate (http://www.phpxtemplate.org)
6: * All methods work exactly as they do with XTemplate, however this template engine performs far better
7: *
8: * http://opensourceame.com
9: *
10: * @package opensourceame
11: * @author David Kelly
12: * @copyright David Kelly 2007
13: * @description zTemplate is a high performance replacement for xTemplate
14: * @version 1.1.8
15: *
16: * distributed under the GPL 2.0, licensing info at http://www.gnu.org/licenses/gpl-2.0.html
17: */
18:
19: if(! class_exists("xtemplate"))
20: {
21: // extend the ztemplate class with some backwards compatible functions for xtemplate
22: //
23: // note that at 2 dec 2005 these are the only functions used by sugar
24: // determined by matching /xtpl->.*(/U in all source pages
25:
26: class xtemplate extends ztemplate
27: {
28: function __construct($file, $alt_include = null, $mainblock='main')
29: {
30: $this->mainblock = $mainblock;
31:
32: parent::__construct($file);
33: }
34:
35: function var_exists($block, $var)
36: {
37: return isset($this->parselines->vbindex[$var]);
38: }
39:
40: function exists($block)
41: {
42: return (! empty($this->parsed[$block])) || (! empty($this->blocks[$block]));
43: }
44:
45:
46: function append ($varname, $name, $val = null)
47: {
48: $this->assign($varname.'.'.$name, $val);
49: }
50:
51: }
52: }
53:
54: /**
55: * zTemplate class
56: */
57: class ztemplate
58: {
59: const version = '1.1.8'; // the version of this class
60:
61: private $template = true;
62: private $cachedblocks = array(); // an array of cached blocks
63: private $blocks = array(); // an array of blocks
64: private $parsed = array(); // an array of parsed blocks
65:
66: protected $stblocks = array();
67:
68: /**
69: * Class constructor
70: *
71: * @param string $file the filename to open and parse
72: * @param array $options an array of config options
73: * @return boolean true on successul load and parse of the template, otherwise false
74: */
75: public function __construct($file = null, $options = null)
76: {
77: // block delimeters kept in an object
78: $this->delim = new stdClass;
79: $this->delim->block = new stdClass;
80: $this->delim->start = '<!--';
81: $this->delim->end = '-->';
82: $this->delim->block->start = 'BEGIN:';
83: $this->delim->block->end = 'END:';
84:
85: $this->_parse_options = new stdClass;
86: $this->_parse_options->line_end = "\n";
87:
88: $this->count = new stdClass;
89: $this->count->parsefile = false;
90: $this->block_order = array();
91:
92: $this->options = new stdClass;
93: $this->options->condensed = false;
94: $this->options->debug = false;
95: $this->options->cache = false;
96: $this->options->strict = false;
97:
98: $this->parselines = new stdClass;
99: $this->parselines->subtemplates = array();
100:
101: $this->log = new stdClass;
102: $this->log->errors = array();
103: $this->log->debug = array();
104:
105: if(! empty($options))
106: $this->set_option($options);
107:
108: if($file != null)
109: {
110: return $this->parse_template_file($file);
111: }
112:
113: return true;
114: }
115:
116: /**
117: * used to to manually load a template if one is not specified when constructing
118: */
119: public function load_template($file)
120: {
121: $this->parse_template_file($file);
122: }
123:
124: /**
125: * Set an option
126: */
127: public function set_option($opt, $val = null)
128: {
129: if( is_array($opt) or is_object($opt))
130: foreach($opt as $key=>$val)
131: $this->set_option($key, $val);
132:
133: switch($opt)
134: {
135: // condense by stripping out unnecessary whitespace
136: case "condensed":
137: $this->_parse_options->line_end = null;
138: $this->options->condensed = true;
139: break;
140:
141: // cache parsed results
142: case "cache":
143: $this->options->cache = true;
144: break;
145:
146: // add debugging
147: case "debug":
148: $this->options->debug = true;
149: $this->debug = array();
150: break;
151:
152: // break on any error (e.g. reference to an unassigned template var)
153: case "strict":
154: $this->options->strict = true;
155: break;
156: }
157:
158: return true;
159:
160: }
161:
162: /**
163: * @return array a list of errors that have occurred
164: */
165: public function errors()
166: {
167: return $this->log->errors;
168: }
169:
170: /**
171: * log an error
172: */
173: private function add_error($message)
174: {
175: $this->log->errors[] = $message;
176: }
177:
178: /**
179: * clear a cached block
180: */
181: private function clear_cache($block)
182: {
183: $this->add_debug("clear cache called for $block");
184:
185: // recursively clear blocks
186: // remove any preparsed block this will affect
187: if( isset($this->cachedblocks[$block]))
188: {
189: {
190: $this->add_debug("delete cache block $block");
191: unset($this->cachedblocks[$block]);
192:
193: while( isset($this->cachedblocks[$this->parents[$block]]))
194: {
195: $this->add_debug("delete cache block $block parent {$this->parents[$block]}");
196: unset($this->cachedblocks[$this->parents[$block]]);
197: $block = $this->parents[$block];
198: }
199: }
200: } else {
201: // return false if no blocks to delete
202: return false;
203: }
204:
205:
206: }
207:
208: /**
209: * assign a variable
210: */
211: public function assign($var, $val)
212: {
213:
214: if(is_array($val) or is_object($val))
215: {
216: foreach($val as $k=>$v)
217: $this->assign("$var.$k", $v);
218:
219: return true;
220: }
221:
222:
223: if( ($this->options->strict) and ! in_array($var, $this->parselines->vars))
224: {
225: $this->add_error("tried to assign non-existent var $var");
226: return false;
227: }
228:
229: // assign the var
230: settype($val, 'string'); // force string to fix problems with 0 values interpreted as null
231: $this->vars[$var] = $val;
232:
233: // recursively delete cached blocks if we are doing caching
234: if($this->options->cache)
235: {
236: foreach($this->parselines->vbindex[$var] as $block)
237: $this->clear_cache($block);
238: }
239:
240: return true;
241: }
242:
243: /**
244: * assign a file that can be included into the current template
245: */
246: public function assign_file($var, $file)
247: {
248: $this->parse_template_file($file, $this->stblocks[$var]);
249: $this->filevars[$var] = $file;
250: }
251:
252: /**
253: * output a block, optionally to a file
254: *
255: * @param string $blockname name of the block to output
256: * @param string $file optional filename to write output to
257: */
258: public function out($blockname = null, $file = null)
259: {
260: if( isset($this->parsed[$blockname]))
261: {
262: if($file == null)
263: {
264: echo($this->parsed[$blockname]);
265: return true;
266: } else {
267: $fh = fopen($file, 'w');
268:
269: if($fh === false)
270: {
271: $this->add_error("could not write $blockname to $file");
272: return false;
273: }
274:
275: fwrite($fh, $this->parsed[$blockname]);
276: //fclose($fh);
277:
278: return true;
279: }
280: } else {
281: $this->add_error("tried to output unparsed block $blockname");
282: }
283:
284: return false;
285: }
286:
287: /**
288: * get a parsed block
289: *
290: * @param string $blockname the name of the block to return
291: */
292: public function get($blockname = null)
293: {
294: return($this->parsed[$blockname]);
295: }
296:
297: /**
298: * print errors inside preformatted tags
299: */
300: public function print_errors()
301: {
302: if(! is_array($this->log->errors))
303: return false;
304:
305: echo '<pre>';
306: foreach($this->log->errors as $line)
307: {
308: print($line."\n");
309: }
310: echo "</pre>";
311: }
312:
313: /**
314: * log a debugging message
315: */
316: private function add_debug($message)
317: {
318: if($this->options->debug)
319: $this->log->debug[] = $message;
320: }
321:
322: /**
323: * parse a block
324: *
325: * @param string $blockname name of the block to parse
326: * @return boolean
327: */
328: public function parse($blockname)
329: {
330: if(! is_string($blockname))
331: {
332: print_r($blockname);
333: exit;
334: }
335:
336: // parse blocks
337: if(! isset($this->parsed[$blockname]))
338: $this->parsed[$blockname] = null;
339:
340: if( isset($this->cachedblocks[$blockname]))
341: {
342: // cached block exists, use it
343: $this->parsed[$blockname] .= $this->cachedblocks[$blockname];
344: $this->add_debug("using cached $blockname");
345: return true;
346: }
347:
348: if( empty($this->blocks[$blockname]))
349: {
350: $this->add_error("could not parse missing block $blockname");
351: return false;
352: }
353:
354: foreach($this->blocks[$blockname] as $key=>$line)
355: {
356: $append = $this->_parse_options->line_end;
357:
358: if( isset($this->parselines->subtemplates[$key]))
359: {
360: $v =& $this->parselines->subtemplates[$key];
361: if( isset($this->filevars[$v]))
362: {
363: $this->reset($this->filevars[$v]);
364: $this->parse($this->filevars[$v]);
365: $this->parsed[$blockname] .= $this->parsed[$this->filevars[$v]];
366: } else {
367: $this->add_error("missing included template $v while parsing block $blockname");
368: }
369:
370: $line = null;
371:
372: }
373:
374: if( isset($this->parselines->vars[$key + 1]))
375: $append = null;
376:
377: if( isset($this->parselines->vars[$key]))
378: {
379: if( isset($this->vars[$this->parselines->vars[$key]]))
380: {
381: $line = $this->vars[$this->parselines->vars[$key]];
382: $append = null;
383: } else {
384: $line = null;
385: $append = null;
386: $this->add_debug("missing var {$this->parselines->vars[$key]} while parsing block {$blockname}");
387: }
388: }
389:
390: if( isset($this->parselines->blocks[$key]))
391: {
392: if( isset($this->parsed[$this->parselines->blocks[$key]]))
393: {
394: $this->parsed[$blockname] .= $this->parsed[$this->parselines->blocks[$key]];
395: unset($this->parsed[$this->parselines->blocks[$key]]);
396: } else {
397: //$this->add_error("no block parsed $match[1]");
398: }
399: $line = null;
400: }
401:
402: if($line != null)
403: $this->parsed[$blockname] .= $line . $append;
404: }
405:
406: if($this->options->cache)
407: {
408: $this->cachedblocks[$blockname] = $this->parsed[$blockname];
409: $this->add_debug("cache block $blockname");
410: }
411:
412: $this->add_debug("parsed block $blockname");
413:
414: return true;
415: }
416:
417: /**
418: * Reset a block
419: *
420: * @param string $block name of the block
421: * @return boolean
422: */
423: public function reset ($block)
424: {
425: if(! isset($this->parsed[$block]))
426: return false;
427:
428: $this->parsed[$block] = null;
429:
430: return true;
431: }
432:
433: /**
434: * Check if a variable has been set
435: *
436: * @param string $var variable name
437: * @return boolean
438: */
439: public function var_is_set($var)
440: {
441: return ! empty($this->vars[$var]);
442: }
443:
444: /**
445: * Get variables from lines in each block
446: */
447: private function parser_get_vars()
448: {
449: foreach(array_keys($this->blocks) as $blockname)
450: if(is_array($this->blocks[$blockname]))
451: foreach($this->blocks[$blockname] as $count=>$line)
452: {
453: do
454: {
455: $reparse = false;
456:
457: if(preg_match("/^(.*)\{([\w_\.]*)\}(.*)$/U", $line, $match))
458: {
459: // found a var, get busy
460: $this->blocks[$blockname][$count] = $match[1]; // replace the line with the left side match
461: $this->blocks[$blockname][++$count] = $match[2]; // new line with the var name
462:
463: $this->parselines->vars[$count] = $match[2]; // keep track of var position
464: $this->parselines->vbindex[$match[2]][$blockname] = $blockname; // variable block index
465: $line = $match[3];
466:
467: $reparse = true;
468: $count++;
469:
470: } else {
471: $this->blocks[$blockname][$count] = $line;
472: }
473:
474: } while($reparse);
475:
476: ksort($this->blocks[$blockname]);
477: }
478: }
479:
480: /**
481: * Add a line to the parsed block
482: *
483: * @param string $block the block name
484: * @param string $line the line of text to add
485: * @return boolean
486: */
487: private function parse_add_line($block, $line)
488: {
489: if(empty($line))
490: return false;
491:
492: if($this->options->condensed)
493: {
494: $line=preg_replace("/^\s*/", null, $line);
495: $line=preg_replace("/\s*$/", null, $line);
496: }
497:
498: $this->count->parsefile += 100;
499:
500: $this->blocks[$block][$this->count->parsefile] = $line;
501:
502: return true;
503: }
504:
505: /**
506: * Parse a template
507: *
508: * @param string $file filename to open and parse
509: * @param string $bp the block path (e.g. main.block1.block2)
510: * @return boolean
511: */
512: private function parse_template_file($file, $bp = null)
513: {
514:
515: if(! is_readable($file))
516: {
517: $this->add_error("could not read $file to parse it");
518: return false;
519: }
520:
521: if(filesize($file) == 0)
522: {
523: $this->add_error("file has zero size");
524: return false;
525: }
526:
527: // init vars
528: $blockpath = null;
529:
530: // read the file
531: $handle = fopen($file, "r");
532:
533: if($handle == false)
534: {
535: die("couldn't open $file");
536: }
537:
538: $this->contents[$file] = explode("\n", fread($handle, filesize($file)));
539: fclose($handle);
540:
541: // break file into blocks
542: foreach($this->contents[$file] as $line)
543: {
544: do
545: {
546: $blockprefix = $bp;
547: if( ($blockpath != null) and ($bp != null))
548: $blockprefix = "{$blockprefix}.";
549:
550: $reparse = false;
551:
552: if(preg_match("/(.*)\{FILE {(.*)\}\}(.*)$/U", $line, $match))
553: {
554: if($blockpath == null)
555: {
556: $this->parse_add_line($file, $match[1]);
557: $this->parse_add_line($file, "%$match[2]%");
558:
559: } else {
560:
561: $this->parse_add_line($blockprefix.$blockpath, $match[1]);
562: $this->parse_add_line($blockprefix.$blockpath, "%$match[2]%");
563: }
564:
565: $this->parselines->subtemplates[$this->count->parsefile] = $match[2];
566:
567: $this->stblocks[$match[2]] = $blockprefix.$blockpath;
568:
569: $line = $match[3];
570:
571: $reparse = true;
572: }
573:
574: // start a block
575: if(preg_match("/^(.*)".$this->delim->start." ".$this->delim->block->start." (.*) ".$this->delim->end."(.*)$/U", $line, $match))
576: {
577: if($blockpath == null)
578: {
579: $this->parse_add_line($file, $match[1]);
580: $this->parse_add_line($file, "@{$blockprefix}{$blockpath}.$match[2]@");
581: } else {
582: $this->parse_add_line($blockprefix.$blockpath, $match[1]);
583: $this->parse_add_line($blockprefix.$blockpath, "@{$blockprefix}{$blockpath}.$match[2]@");
584: }
585:
586: if($blockpath != null)
587: {
588: $this->parents["{$blockpath}.{$match[2]}"] = $blockprefix.$blockpath;
589: $blockpath .= ".";
590: }
591: $blockpath .= $match[2];
592:
593: $blockprefix = $bp;
594: if( ($blockpath != null) and ($bp != null))
595: $blockprefix = "{$blockprefix}.";
596:
597: $this->parselines->blocks[$this->count->parsefile] = $blockprefix.$blockpath;
598: $this->parselines->bindex[$blockprefix.$blockpath] = $this->count->parsefile;
599:
600: array_unshift($this->block_order, $blockprefix.$blockpath);
601: $line = $match[3];
602:
603: $reparse = true;
604: }
605:
606: // match a block end
607: if(preg_match("/^(.*)".$this->delim->start." ".$this->delim->block->end." (.*) ".$this->delim->end."(.*)$/U", $line, $match))
608: {
609: if(preg_match("/$match[2]$/", $blockpath))
610: {
611: if($blockpath == null)
612: $this->parse_add_line($file, $match[1]);
613: else
614: $this->parse_add_line($blockprefix.$blockpath, $match[1]);
615:
616: $blockpath = substr($blockpath, 0, -(strlen($match[2]))-1);
617:
618: $line = $match[3];
619: } else {
620: $this->add_error("tried to end block $match[2] within {$blockprefix}{$blockpath}");
621: $this->print_errors();
622: die;
623: }
624: $reparse = true;
625: }
626:
627: } while($reparse);
628:
629: if($blockpath == null)
630: $this->parse_add_line($file, $line);
631: else
632: $this->parse_add_line($blockprefix.$blockpath, $line);
633: }
634:
635: // add file block to block order list
636: $this->block_order[]= $file;
637:
638: // now get the variables
639: $this->parser_get_vars();
640:
641: // free up some ram if not debugging
642: if($this->options->debug)
643: unset($this->contents[$file]);
644: }
645:
646: /**
647: * Return the text of a block
648: *
649: * @param string $blockname name of the block
650: * @return string
651: */
652: public function text($blockname)
653: {
654: return $this->parsed[ isset($blockname) ? $blockname : $this->mainblock ];
655: }
656:
657: }
658:
659: