]> git.p6c8.net - form-email.git/blob - form-email/class.Template.php
ad6dc0ecc98875c3f38d32d2a53b3e5b6291d946
[form-email.git] / form-email / class.Template.php
1 <?php
2
3 #
4 # Template (Version 2.0)
5 #
6 # Klasse zum Parsen von Templates
7 #
8 # Autor: Patrick Canterino <patrick@patshaping.de>
9 # Letzte Aenderung: 3.7.2006
10 #
11
12 class Template
13 {
14 var $file;
15 var $template;
16 var $original;
17 var $vars = array();
18 var $defined_vars = array();
19 var $loop_vars = array();
20
21 # get_template()
22 #
23 # Kompletten Vorlagentext zurueckgeben
24 #
25 # Parameter: -keine-
26 #
27 # Rueckgabe: Kompletter Vorlagentext (String)
28
29 function get_template()
30 {
31 return $this->template;
32 }
33
34 # set_template()
35 #
36 # Kompletten Vorlagentext aendern
37 #
38 # Parameter: Vorlagentext
39 #
40 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
41
42 function set_template($text)
43 {
44 $this->template = $text;
45 }
46
47 # add_text()
48 #
49 # Vorlagentext ans Template-Objekt anhaengen
50 #
51 # Parameter: Vorlagentext
52 #
53 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
54
55 function add_text($text)
56 {
57 $this->set_template($this->get_template().$text);
58 }
59
60 # read_file()
61 #
62 # Einlesen einer Vorlagendatei und {INCLUDE}-Anweisungen ggf. verarbeiten
63 # (Text wird an bereits vorhandenen Text angehaengt)
64 #
65 # Parameter: 1. Datei zum Einlesen
66 # 2. Status-Code (Boolean):
67 # true => {INCLUDE}-Anweisungen nicht verarbeiten
68 # false => {INCLUDE}-Anweisungen verarbeiten (Standard)
69 #
70 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
71
72 function read_file($file,$not_include=0)
73 {
74 $this->file = $file;
75
76 if(filesize($file) > 0)
77 {
78 $fp = fopen($file,'r');
79 if(!$fp) die;
80 $content = fread($fp,filesize($file));
81 fclose($fp);
82 }
83 else $content = '';
84
85 $this->add_text($content);
86 $this->save_state();
87
88 if(!$not_include) $this->parse_includes();
89 }
90
91 # set_var()
92 #
93 # Wert einer Variable setzen
94 #
95 # Parameter: 1. Name der Variable
96 # 2. Wert, den die Variable erhalten soll
97 #
98 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
99
100 function set_var($var,$content)
101 {
102 $this->vars[$var] = $content;
103 }
104
105 # get_var()
106 #
107 # Wert einer Variable zurueckgeben
108 #
109 # Parameter: (optional) Variablenname
110 #
111 # Rueckgabe: Wert der Variable;
112 # wenn die Variable nicht existiert, false;
113 # wenn kein Variablenname angegeben wurde, wird ein
114 # Array mit den Variablennamen zurueckgegeben
115
116 function get_var($var=false)
117 {
118 if($var !== false)
119 {
120 if(isset($this->vars[$var]))
121 {
122 return $this->vars[$var];
123 }
124 else
125 {
126 return false;
127 }
128 }
129 else
130 {
131 return array_keys($this->vars);
132 }
133 }
134
135 # set_loop_data()
136 #
137 # Daten fuer eine Schleife setzen
138 #
139 # Parameter: 1. Name der Schleife
140 # 2. Array mit den Arrays mit den Variablen fuer
141 # die Schleifendurchgaenge
142 #
143 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
144
145 function set_loop_data($loop,$data)
146 {
147 $this->loop_vars[$loop] = $data;
148 }
149
150 # add_loop_data()
151 #
152 # Daten fuer einen Schleifendurchgang hinzufuegen
153 #
154 # Parameter: 1. Name der Schleife
155 # 2. Array mit den Variablen fuer den
156 # Schleifendurchgang
157 #
158 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
159
160 function add_loop_data($loop,$data)
161 {
162 if(isset($this->loop_vars[$loop]) && is_array($this->loop_vars[$loop]))
163 {
164 array_push($this->loop_vars[$loop],$data);
165 }
166 else
167 {
168 $this->loop_vars[$loop] = array($data);
169 }
170 }
171
172 # parse()
173 #
174 # In der Template definierte Variablen auslesen, Variablen
175 # ersetzen, {IF}- und {TRIM}-Bloecke parsen
176 #
177 # Parameter: -nichts-
178 #
179 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
180
181 function parse()
182 {
183 # Zuerst die Schleifen parsen
184
185 if(is_array($this->loop_vars) && ($loops = array_keys($this->loop_vars)))
186 {
187 foreach($loops as $loop)
188 {
189 $this->parse_loop($loop);
190 }
191 }
192
193 # Normale Variablen durchgehen
194
195 if(($vars = $this->get_var()) !== false && is_array($vars))
196 {
197 foreach($vars as $var)
198 {
199 $val = $this->get_var($var);
200
201 $this->parse_if_block($var,$val);
202
203 if(is_array($val))
204 {
205 $this->fillin_array($var,$val);
206 }
207 else
208 {
209 $this->fillin($var,$val);
210 }
211
212 unset($val);
213 }
214 }
215
216 # Jetzt dasselbe mit denen, die direkt in der Template-Datei definiert
217 # sind, machen. Ich weiss, dass das eine ziemlich unsaubere Loesung ist,
218 # aber es funktioniert
219
220 $this->get_defined_vars();
221
222 foreach($this->defined_vars as $var)
223 {
224 $val = $this->get_var($var);
225
226 $this->parse_if_block($var,$val);
227 $this->fillin($var,$val);
228
229 unset($val);
230 }
231
232 # {TRIM}-Bloecke entfernen
233
234 $this->parse_trim_blocks();
235 }
236
237 # fillin()
238 #
239 # Variablen durch Text ersetzen
240 #
241 # Parameter: 1. Variable zum Ersetzen
242 # 2. Text, durch den die Variable ersetzt werden soll
243 #
244 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
245
246 function fillin($var,$text)
247 {
248 $template = $this->get_template();
249 $template = str_replace('{'.$var.'}',$text,$template);
250
251 $this->set_template($template);
252 }
253
254 # fillin_array()
255 #
256 # Variable durch Array ersetzen
257 #
258 # Parameter: 1. Variable zum Ersetzen
259 # 2. Array, durch das die Variable ersetzt werden soll
260 # 3. Zeichenkette, mit der das Array verbunden werden soll
261 # (Standard: '')
262 #
263 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
264
265 function fillin_array($var,$array,$glue='')
266 {
267 $this->fillin($var,implode($glue,$array));
268 }
269
270 # to_file()
271 #
272 # Template in Datei schreiben
273 #
274 # Parameter: Datei-Handle
275 #
276 # Rueckgabe: Status-Code (Boolean)
277
278 function to_file($handle)
279 {
280 return @fwrite($handle,$this->get_template());
281 }
282
283 # reset()
284 #
285 # Den gesicherten Stand des Template-Textes sichern
286 #
287 # Parameter: -nichts-
288 #
289 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
290
291 function reset()
292 {
293 $this->template = $this->original;
294 }
295
296 # save_state()
297 #
298 # Aktuellen Stand des Template-Textes sichern
299 # (alte Sicherung wird ueberschrieben)
300 #
301 # Parameter: -nichts-
302 #
303 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
304
305 function save_state()
306 {
307 $this->original = $this->template;
308 }
309
310 # parse_loop()
311 #
312 # Eine Schleife parsen
313 #
314 # Parameter: Name der Schleife
315 #
316 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
317
318 function parse_loop($name)
319 {
320 $template = $this->get_template();
321 if(strpos($template,'{LOOP '.$name.'}') === false) return;
322
323 $offset = 0;
324 $name_len = strlen($name);
325
326 while(($begin = strpos($template,'{LOOP '.$name.'}',$offset)) !== false)
327 {
328 if(($end = strpos($template,'{ENDLOOP}',$begin+6+$name_len)) !== false)
329 {
330 $block = substr($template,$begin,$end+9-$begin);
331 $content = substr($block,$name_len+7,-9);
332
333 $parsed_block = '';
334
335 for($x=0;$x<count($this->loop_vars[$name]);$x++)
336 {
337 $loop_data = $this->loop_vars[$name][$x];
338 $loop_vars = array_keys($loop_data);
339
340 $ctpl = new Template;
341 $ctpl->set_template($content);
342
343 foreach($loop_vars as $loop_var)
344 {
345 $ctpl->set_var($name.'.'.$loop_var,$loop_data[$loop_var]);
346 }
347
348 $ctpl->parse();
349 $parsed_block .= $ctpl->get_template();
350
351 unset($ctpl);
352 }
353
354 $template = str_replace($block,$parsed_block,$template);
355 $offset = $begin+strlen($parsed_block);
356 }
357 else break;
358 }
359
360 $this->set_template($template);
361 }
362
363 # get_defined_vars()
364 #
365 # In der Template-Datei definierte Variablen auslesen
366 #
367 # Parameter: -nichts-
368 #
369 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
370
371 function get_defined_vars()
372 {
373 $template = $this->get_template();
374 if(strpos($template,'{DEFINE ') === false) return;
375
376 $offset = 0;
377
378 while(strpos($template,'{DEFINE ',$offset) !== false)
379 {
380 $begin = strpos($template,'{DEFINE ',$offset)+8;
381 $offset = $begin;
382
383 $name = '';
384 $content = '';
385
386 $var_open = 0;
387 $name_found = 0;
388 $define_block = 0;
389
390 for($x=$begin;$x<strlen($template);$x++)
391 {
392 if($template[$x] == "\012" || $template[$x] == "\015")
393 {
394 # Wenn in einem {DEFINE}-Block ein Zeilenumbruch gefunden wird,
395 # brechen wir mit dem Parsen des Blockes ab
396
397 break;
398 }
399
400 if($var_open == 1)
401 {
402 if($template[$x] == '"')
403 {
404 # Der Inhalt der Variable ist hier zu Ende
405
406 $var_open = 0;
407
408 if($template[$x+1] == '}')
409 {
410 # Hier ist der Block zu Ende
411
412 if($this->get_var($name) === false)
413 {
414 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
415
416 $this->set_var($name,$content);
417 array_push($this->defined_vars,$name);
418 }
419
420 # {DEFINE}-Block entfernen
421
422 $pre = substr($template,0,$begin-8);
423 $post = substr($template,$x+2);
424
425 $template = $pre.$post;
426
427 # Fertig!
428
429 $offset = strlen($pre);
430 break;
431 }
432 }
433 elseif($template[$x] == '\\')
434 {
435 # Ein Backslash wurde gefunden, er dient zum Escapen von Zeichen
436
437 if($template[$x+1] == 'n')
438 {
439 # "\n" in Zeilenumbrueche umwandeln
440
441 $content .= "\n";
442 }
443 else $content .= $template[$x+1];
444
445 $x++;
446 }
447 else $content .= $template[$x];
448 }
449 else
450 {
451 if($name_found == 1)
452 {
453 if($var_open == 0)
454 {
455 if($template[$x] == '"') $var_open = 1;
456 else break;
457 }
458 }
459 else
460 {
461 # Variablennamen auslesen
462
463 if($template[$x] == '}' && $name != '')
464 {
465 # Wir haben einen {DEFINE}-Block
466
467 $name_found = 1;
468 $define_block = 1;
469
470 # Alles ab hier sollte mit dem Teil verbunden werden, der das
471 # {DEFINE} in einer Zeile verarbeitet
472
473 # Der Parser fuer {DEFINE}-Bloecke ist nicht rekursiv, was auch
474 # nicht noetig sein sollte
475
476 if(($end = strpos($template,'{ENDDEFINE}',$x)) !== false)
477 {
478 $x++;
479
480 $content = substr($template,$x,$end-$x);
481
482 if($this->get_var($name) === false)
483 {
484 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
485
486 $this->set_var($name,$content);
487 array_push($this->defined_vars,$name);
488 }
489
490 $pre = substr($template,0,$begin-8);
491 $post = substr($template,$end+11);
492
493 $template = $pre.$post;
494
495 # Fertig!
496
497 $offset = strlen($pre);
498 break;
499 }
500 else break;
501 }
502 elseif($template[$x] != ' ')
503 {
504 $name .= $template[$x];
505 }
506 elseif($name != '')
507 {
508 $name_found = 1;
509 }
510 else break;
511 }
512 }
513 }
514 }
515
516 $this->set_template($template);
517 }
518
519 # parse_if_block()
520 #
521 # IF-Bloecke verarbeiten
522 #
523 # Parameter: 1. Name des IF-Blocks (das, was nach dem IF steht)
524 # 2. Status-Code (true => Inhalt anzeigen
525 # false => Inhalt nicht anzeigen
526 # 3. true => Verneinten Block nicht parsen
527 # false => Verneinten Block parsen (Standard)
528 #
529 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
530
531 function parse_if_block($name,$state,$no_negate=0)
532 {
533 $template = $this->get_template();
534
535 $count = 0;
536
537 while(strpos($template,'{IF '.$name.'}') !== false)
538 {
539 # Das alles hier ist nicht wirklich elegant geloest...
540 # ... aber solange es funktioniert... ;-)
541
542 $count++;
543
544 $start = strpos($template,'{IF '.$name.'}');
545 $tpl_tmp = substr($template,$start);
546 $splitted = explode('{ENDIF}',$tpl_tmp);
547
548 $block = ''; # Kompletter bedingter Block
549 $ifs = 0; # IF-Zaehler (wird fuer jedes IF erhoeht und fuer jedes ENDIF erniedrigt)
550
551 # {IF}
552
553 for($x=0;$x<count($splitted);$x++)
554 {
555 if($x == count($splitted)-1) die('Nesting error found while parsing IF block "'.$name.'" nr. '.$count.' in template file "'.$this->file.'"');
556
557 $ifs += substr_count($splitted[$x],'{IF '); # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
558 $ifs--; # Zaehler um 1 erniedrigen
559 $block .= $splitted[$x].'{ENDIF}'; # Daten zum Block hinzufuegen
560
561 if($ifs == 0)
562 {
563 # Zaehler wieder 0, also haben wir das Ende des IF-Blocks gefunden :-))
564
565 break;
566 }
567 }
568
569 $if_block = substr($block,strlen($name)+5,-7); # Alles zwischen {IF} und {ENDIF}
570
571 # {ELSE}
572
573 $else_block = ''; # Alles ab {ELSE}
574 $ifs = 0; # IF-Zaehler
575
576 $splitted = explode('{ELSE}',$if_block);
577
578 for($x=0;$x<count($splitted);$x++)
579 {
580 $ifs += substr_count($splitted[$x],'{IF '); # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
581 $ifs -= substr_count($splitted[$x],'{ENDIF}'); # Vom Zaehler jedes Vorkommen von ENDIF abziehen
582
583 if($ifs == 0)
584 {
585 # Zaehler 0, also haben wir das Ende des IF-Abschnitts gefunden
586
587 # Aus dem Rest den ELSE-Block zusammenbauen
588
589 for($y=$x+1;$y<count($splitted);$y++)
590 {
591 $else_block .= '{ELSE}'.$splitted[$y];
592 }
593
594 if($else_block)
595 {
596 $if_block = substr($if_block,0,strlen($if_block)-strlen($else_block));
597 $else_block = substr($else_block,6);
598 }
599
600 break;
601 }
602 }
603
604 # Block durch die jeweiligen Daten ersetzen
605
606 $replacement = ($state) ? $if_block : $else_block;
607
608 $template = str_replace($block,$replacement,$template);
609 }
610
611 $this->set_template($template);
612
613 # Evtl. verneinte Form parsen
614
615 if(!$no_negate)
616 {
617 $this->parse_if_block('!'.$name,!$state,1);
618 }
619 }
620
621 # parse_trim_blocks()
622 #
623 # {TRIM}-Bloecke parsen
624 #
625 # Dieser Parser ist nicht rekursiv, was auch nicht
626 # noetig sein sollte.
627 #
628 # Parameter: -nichts-
629 #
630 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
631
632 function parse_trim_blocks()
633 {
634 $template = $this->get_template();
635 if(strpos($template,'{TRIM}') === false) return;
636
637 $offset = 0;
638
639 while(($begin = strpos($template,'{TRIM}',$offset)) !== false)
640 {
641 if(($end = strpos($template,'{ENDTRIM}',$begin+6)) !== false)
642 {
643 $block = substr($template,$begin,$end+9-$begin);
644 $content = substr($block,6,-9);
645
646 $trimmed = trim($content);
647
648 $template = str_replace($block,$trimmed,$template);
649
650 $offset = $begin+strlen($trimmed);
651 }
652 else break;
653 }
654
655 $this->set_template($template);
656 }
657
658 # parse_condtag()
659 #
660 # Bedingungstags in einem Vorlagentext verarbeiten
661 #
662 # Parameter: 1. Tagname
663 # 2. Status-Code (true => Tag-Inhalt anzeigen
664 # false => Tag-Inhalt nicht anzeigen
665 #
666 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
667
668 function parse_condtag($condtag,$state)
669 {
670 $template = $this->get_template();
671
672 while(strpos($template,'<'.$condtag.'>') !== false)
673 {
674 $start = strpos($template,'<'.$condtag.'>'); # Beginn des Blocks
675 $end = strpos($template,'</'.$condtag.'>')+strlen($condtag)+3; # Ende des Blocks
676
677 $extract = substr($template,$start,$end-$start); # Kompletten Bedingungsblock extrahieren...
678
679 $replacement = ($state) ? substr($extract,strlen($condtag)+2,0-strlen($condtag)-3) : '';
680
681 $template = str_replace($extract,$replacement,$template); # Block durch neue Daten ersetzen
682 }
683 $this->set_template($template);
684 }
685
686 # parse_includes()
687 #
688 # {INCLUDE}-Anweisungen verarbeiten
689 #
690 # Parameter: -nichts-
691 #
692 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
693
694 function parse_includes()
695 {
696 $template = $this->get_template();
697 if(strpos($template,'{INCLUDE ') === false) return;
698
699 $offset = 0;
700
701 $y = 0;
702
703 while(($begin = strpos($template,'{INCLUDE ',$offset)) !== false)
704 {
705 $y++;
706
707 $start = $begin+9;
708 $offset = $start;
709 $long = 0;
710
711 if($template[$start] == '"')
712 {
713 $long = 1;
714 $start++;
715 }
716
717 $file = '';
718 $skip = 0;
719
720 for($x=$start;$x<strlen($template);$x++)
721 {
722 if($template[$x] == "\012" || $template[$x] == "\015")
723 {
724 $skip = 1;
725 break;
726 }
727 elseif($long == 0 && $template[$x] == ' ')
728 {
729 $skip = 1;
730 break;
731 }
732 elseif($long == 1 && $template[$x] == '"')
733 {
734 if($template[$x+1] != '}') $skip = 1;
735 break;
736 }
737 elseif($long == 0 && $template[$x] == '}')
738 {
739 break;
740 }
741 else
742 {
743 $file .= $template[$x];
744 }
745 }
746
747 if($skip == 1) continue;
748
749 if($file != '')
750 {
751 $filepath = $file;
752
753 $is_absolute = (strtoupper(substr(PHP_OS,0,3)) === 'WIN')
754 ? preg_match('!^([a-z]:)?/!i',$file)
755 : preg_match('!^/!',$file);
756
757 if(!$is_absolute)
758 {
759 if(!empty($this->file)) $dir = dirname($this->file);
760 else $dir = '.';
761
762 $dir = str_replace('\\','/',$dir);
763
764 if(!preg_match('!/+$!',$dir)) $dir .= '/';
765
766 $filepath = $dir.$file;
767 }
768
769 if(is_file($filepath))
770 {
771 $inc = new Template;
772 $inc->read_file($filepath);
773
774 $end = ($long == 1)
775 ? $start + strlen($file) + 2
776 : $start + strlen($file) + 1;
777
778 $pre = substr($template,0,$begin);
779 $post = substr($template,$end);
780
781 $template = $pre.$inc->get_template().$post;
782 $offset = strlen($pre)+strlen($inc->get_template());
783
784 unset($inc);
785 }
786 }
787 }
788
789 $this->set_template($template);
790 }
791 }
792
793 #
794 ### Ende ###
795
796 ?>

patrick-canterino.de