]>
git.p6c8.net - template-class.git/blob - Template.pm
4 # Template (Version 2.5)
6 # Klasse zum Parsen von Templates
8 # Autor: Patrick Canterino <patrick@patshaping.de>
9 # Letzte Aenderung: 25.11.2011
11 # Copyright (C) 2002-2011 Patrick Canterino
13 # Diese Datei kann unter den Bedingungen der "Artistic License 2.0"
14 # weitergegeben und / oder veraendert werden.
16 # http://www.opensource.org/licenses/artistic-license-2.0
30 # Rueckgabe: Template-Objekt
34 my $self = {file
=> '', template
=> '', original
=> '', old_parsing
=> 0, vars
=> {}, defined_vars
=> [], loop_vars
=> {}};
35 return bless($self,$class);
40 # Kompletten Vorlagentext zurueckgeben
44 # Rueckgabe: Kompletter Vorlagentext (String)
47 return shift->{'template'};
52 # Kompletten Vorlagentext aendern
54 # Parameter: Vorlagentext
56 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
59 my ($self,$template) = @_;
60 $self->{'template'} = $template;
65 # Vorlagentext ans Template-Objekt anhaengen
67 # Parameter: Vorlagentext
69 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
72 my ($self,$text) = @_;
73 $self->set_template($self->get_template.$text);
78 # Einlesen einer Vorlagendatei und {INCLUDE}-Anweisungen ggf. verarbeiten
79 # (Text wird an bereits vorhandenen Text angehaengt)
81 # Parameter: 1. Datei zum Einlesen
82 # 2. Status-Code (Boolean):
83 # true => {INCLUDE}-Anweisungen nicht verarbeiten
84 # false => {INCLUDE}-Anweisungen verarbeiten (Standard)
86 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
89 my ($self,$file,$not_include) = @_;
92 $self->{'file'} = $file;
94 open(FILE
,'<'.$file) or croak
"Open $file: $!";
95 read(FILE
, my $content, -s
$file);
96 close(FILE
) or croak
"Closing $file: $!";
98 $self->add_text($content);
101 $self->parse_includes unless($not_include);
106 # Wert einer Variable setzen
108 # Parameter: 1. Name der Variable
109 # 2. Wert, den die Variable erhalten soll
111 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
114 my ($self,$var,$content) = @_;
115 $self->{'vars'}->{$var} = $content;
120 # Wert einer Variable zurueckgeben
122 # Parameter: (optional) Variablenname
124 # Rueckgabe: Wert der Variable;
125 # wenn die Variable nicht existiert, false;
126 # wenn kein Variablenname angegeben wurde, wird ein
127 # Array mit den Variablennamen zurueckgegeben
130 my ($self,$var) = @_;
133 if($self->{'vars'}->{$var}) {
134 return $self->{'vars'}->{$var};
141 return keys %{$self->{'vars'}};
147 # Komplettes Variablen-Array mit einem anderen Array ueberschreiben
151 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
154 my ($self,$vars) = @_;
155 $self->{'vars'} = $vars;
160 # Zum bestehenden Variablen-Array weitere Variablen in Form eines Arrays
165 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
168 my ($self,$vars) = @_;
169 $self->{'vars'} = {(%{$self->{'vars'}}, %$vars)};
171 #while(my ($name,$content) = each(%$vars)) {
172 # $self->{'vars'}->{$name} = $content;
178 # Daten fuer eine Schleife setzen
180 # Parameter: 1. Name der Schleife
181 # 2. Array-Referenz mit den Hash-Referenzen mit
182 # den Variablen fuer die Schleifendurchgaenge
184 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
186 sub set_loop_data
($$) {
187 my ($self,$loop,$data) = @_;
188 $self->{'loop_vars'}->{$loop} = $data;
193 # Daten fuer einen Schleifendurchgang hinzufuegen
195 # Parameter: 1. Name der Schleife
196 # 2. Hash-Referenz mit den Variablen fuer den
199 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
201 sub add_loop_data
($$) {
202 my ($self,$loop,$data) = @_;
204 if($self->{'loop_vars'}->{$loop} && ref($self->{'loop_vars'}->{$loop}) eq 'ARRAY') {
205 push(@
{$self->{'loop_vars'}->{$loop}},$data);
208 $self->{'loop_vars'}->{$loop} = [$data];
214 # In der Template definierte Variablen auslesen, Variablen
215 # ersetzen, {IF}- und {TRIM}-Bloecke parsen
217 # Parameter: -nichts-
219 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
223 return $self->parse_old if($self->{'old_parsing'});
225 # Zuerst die Schleifen parsen
227 if($self->{'loop_vars'} && (my @loops = keys(%{$self->{'loop_vars'}}))) {
228 foreach my $loop(@loops) {
229 $self->parse_loop($loop);
233 # In Template-Datei definierte Variablen auslesen
235 $self->get_defined_vars;
239 my @vars = $self->get_var;
241 if(defined(@vars) && scalar(@vars) > 0) {
242 $self->parse_if_blocks;
246 # {TRIM}-Bloecke entfernen
248 $self->parse_trim_blocks;
253 # In der Template definierte Variablen auslesen, Variablen
254 # ersetzen, {IF}- und {TRIM}-Bloecke parsen
257 # Parameter: -nichts-
259 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
264 # Zuerst die Schleifen parsen
266 if($self->{'loop_vars'} && (my @loops = keys(%{$self->{'loop_vars'}}))) {
267 foreach my $loop(@loops) {
268 $self->parse_loop($loop);
272 # Normale Variablen durchgehen
274 foreach my $var($self->get_var) {
275 my $val = $self->get_var($var);
277 $self->parse_if_block($var,$val);
279 if(ref($val) eq 'ARRAY') {
280 $self->fillin_array($var,$val);
283 $self->fillin($var,$val);
287 # Jetzt dasselbe mit denen, die direkt in der Template-Datei definiert
288 # sind, machen. Ich weiss, dass das eine ziemlich unsaubere Loesung ist,
289 # aber es funktioniert
291 $self->get_defined_vars;
293 foreach my $var(@
{$self->{'defined_vars'}}) {
294 my $val = $self->get_var($var);
296 $self->parse_if_block($var,$val);
297 $self->fillin($var,$val);
300 # {TRIM}-Bloecke entfernen
302 $self->parse_trim_blocks;
307 # Variablen durch Text ersetzen
309 # Parameter: 1. Variable zum Ersetzen
310 # 2. Text, durch den die Variable ersetzt werden soll
312 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
315 my ($self,$var,$text) = @_;
317 $text = '' unless defined $text; # Um Fehler zu vermeiden
319 my $template = $self->get_template;
320 $template = str_replace
('{'.$var.'}',$text,$template);
322 $self->set_template($template);
327 # Variable durch Array ersetzen
329 # Parameter: 1. Variable zum Ersetzen
330 # 2. Array-Referenz, durch die die Variable ersetzt werden soll
331 # 3. Zeichenkette, mit der das Array verbunden werden soll
334 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
336 sub fillin_array
($$;$) {
337 my ($self,$var,$array,$glue) = @_;
338 $glue = '' unless defined $glue;
340 $self->fillin($var,join($glue,@
$array));
345 # Variablen eine nach der anderen ersetzen. Sollte in einer Variable eine
346 # andere Variable auftauchen, so wird diese nicht ersetzt.
348 # Parameter: Array mit zu parsenden Variablen (optional)
350 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
352 sub replace_vars
(;@
) {
353 my ($self,@valid) = @_;
354 my $template = $self->get_template;
356 my @valid_vars = (@valid) ?
@valid : $self->get_var;
358 for(my $x=0;$x<length($template);$x++) {
359 if(substr($template,$x,1) eq '{') {
360 foreach my $var(@valid_vars) {
361 # Pruefen, ob hier eine gueltige Variable beginnt
363 if(substr($template,$x+1,length($var)+1) eq $var.'}') {
365 my $val = $self->get_var($var);
367 if(ref($val) eq 'ARRAY') {
368 $content = join('',@
$val);
374 # Daten vor und nach der Variable
376 my $pre = substr($template,0,$x);
377 my $post = substr($template,length($pre)+2+length($var));
379 # Alles neu zusammensetzen
381 $template = $pre.$content.$post;
385 $x = length($pre.$content)-1;
391 $self->set_template($template);
396 # Template in Datei schreiben
398 # Parameter: Datei-Handle
400 # Rueckgabe: Status-Code (Boolean)
403 my ($self,$handle) = @_;
404 return print $handle $self->get_template;
409 # Den gesicherten Stand des Template-Textes wiederherstellen
411 # Parameter: -nichts-
413 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
417 $self->{'template'} = $self->{'original'};
422 # Aktuellen Stand des Template-Textes sichern
423 # (alte Sicherung wird ueberschrieben)
425 # Parameter: -nichts-
427 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
431 $self->{'original'} = $self->{'template'};
436 # Eine Schleife parsen
438 # Parameter: Name der Schleife
440 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
443 my ($self,$name) = @_;
445 my $template = $self->get_template;
446 return if(index($template,'{LOOP '.$name.'}') == -1);
449 my $name_len = length($name);
451 while((my $begin = index($template,'{LOOP '.$name.'}',$offset)) != -1) {
452 if((my $end = index($template,'{ENDLOOP}',$begin+6+$name_len)) != -1) {
453 my $block = substr($template,$begin,$end+9-$begin);
454 my $content = substr($block,$name_len+7,-9);
456 my $parsed_block = '';
458 for(my $x=0;$x<scalar @
{$self->{'loop_vars'}->{$name}};$x++) {
459 my $loop_data = $self->{'loop_vars'}->{$name}->[$x];
460 my @loop_vars = keys(%$loop_data);
462 my $ctpl = new Template
;
463 $ctpl->set_template($content);
465 foreach my $loop_var(@loop_vars) {
466 $ctpl->set_var($name.'.'.$loop_var,$loop_data->{$loop_var});
469 if($self->{'old_parsing'}) {
476 $parsed_block .= $ctpl->get_template;
481 $template = str_replace
($block,$parsed_block,$template);
482 $offset = $begin+length($parsed_block);
489 $self->set_template($template);
494 # In der Template-Datei definierte Variablen auslesen
496 # Parameter: -nichts-
498 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
500 sub get_defined_vars
{
503 my $template = $self->get_template;
504 return if(index($template,'{DEFINE ') == -1);
508 while(index($template,'{DEFINE ',$offset) != -1) {
509 my $begin = index($template,'{DEFINE ',$offset)+8;
517 my $define_block = 0;
519 for(my $x=$begin;$x<length($template);$x++) {
520 if(substr($template,$x,1) eq "\012" || substr($template,$x,1) eq "\015") {
521 # Wenn in einem {DEFINE}-Block ein Zeilenumbruch gefunden wird,
522 # brechen wir mit dem Parsen des Blockes ab
528 if(substr($template,$x,1) eq '"') {
529 # Der Inhalt der Variable ist hier zu Ende
533 if(substr($template,$x+1,1) eq '}') {
534 # Hier ist der Block zu Ende
536 if(not defined $self->get_var($name)) {
537 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
539 $self->set_var($name,$content);
540 push(@
{$self->{'defined_vars'}},$name);
543 # {DEFINE}-Block entfernen
545 my $pre = substr($template,0,$begin-8);
546 my $post = substr($template,$x+2);
548 $template = $pre.$post;
552 $offset = length($pre);
556 elsif(substr($template,$x,1) eq '\\') {
557 # Ein Backslash wurde gefunden, er dient zum Escapen von Zeichen
559 if(substr($template,$x+1,1) eq 'n') {
560 # "\n" in Zeilenumbrueche umwandeln
565 $content .= substr($template,$x+1,1);
571 $content .= substr($template,$x,1);
575 if($name_found == 1) {
577 if(substr($template,$x,1) eq '"') {
586 # Variablennamen auslesen
588 if(substr($template,$x,1) eq '}' && $name ne '') {
589 # Wir haben einen {DEFINE}-Block
594 # Alles ab hier sollte mit dem Teil verbunden werden, der das
595 # {DEFINE} in einer Zeile verarbeitet
597 # Der Parser fuer {DEFINE}-Bloecke ist nicht rekursiv, was auch
598 # nicht noetig sein sollte
600 if((my $end = index($template,'{ENDDEFINE}',$x)) != -1) {
603 $content = substr($template,$x,$end-$x);
605 if(not defined $self->get_var($name)) {
606 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
608 $self->set_var($name,$content);
609 push(@
{$self->{'defined_vars'}},$name);
612 my $pre = substr($template,0,$begin-8);
613 my $post = substr($template,$end+11);
615 $template = $pre.$post;
619 $offset = length($pre);
626 elsif(substr($template,$x,1) ne ' ') {
627 $name .= substr($template,$x,1);
629 elsif(substr($template,$x,1) ne '') {
640 $self->set_template($template);
645 # IF-Bloecke verarbeiten
647 # Parameter: 1. Name des IF-Blocks (das, was nach dem IF steht)
648 # 2. Status-Code (true => Inhalt anzeigen
649 # false => Inhalt nicht anzeigen
650 # 3. true => Verneinten Block nicht parsen
651 # false => Verneinten Block parsen (Standard)
653 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
655 sub parse_if_block
($$;$) {
656 my ($self,$name,$state,$no_negate) = @_;
657 my $template = $self->get_template;
661 while(index($template,'{IF '.$name.'}') >= 0) {
662 # Das alles hier ist nicht wirklich elegant geloest...
663 # ... aber solange es funktioniert... ;-)
667 my $start = index($template,'{IF '.$name.'}');
668 my $tpl_tmp = substr($template,$start);
669 my @splitted = explode
('{ENDIF}',$tpl_tmp);
671 my $block = ''; # Kompletter bedingter Block
672 my $ifs = 0; # IF-Zaehler (wird fuer jedes IF erhoeht und fuer jedes ENDIF erniedrigt)
676 for(my $x=0;$x<@splitted;$x++) {
677 croak
'Nesting error found while parsing IF block "'.$name.'" nr. '.$count.' in template file "'.$self->{'file'}.'"' if($x == $#splitted);
679 $ifs += substr_count
($splitted[$x],'{IF '); # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
680 $ifs--; # Zaehler um 1 erniedrigen
681 $block .= $splitted[$x].'{ENDIF}'; # Daten zum Block hinzufuegen
684 # Zaehler wieder 0, also haben wir das Ende des IF-Blocks gefunden :-))
690 my $if_block = substr($block,length($name)+5,-7); # Alles zwischen {IF} und {ENDIF}
694 my $else_block = ''; # Alles ab {ELSE}
695 $ifs = 0; # IF-Zaehler
697 @splitted = explode
('{ELSE}',$if_block);
699 for(my $x=0;$x<@splitted;$x++) {
700 $ifs += substr_count
($splitted[$x],'{IF '); # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
701 $ifs -= substr_count
($splitted[$x],'{ENDIF}'); # Vom Zaehler jedes Vorkommen von ENDIF abziehen
704 # Zaehler 0, also haben wir das Ende des IF-Abschnitts gefunden
706 # Aus dem Rest den ELSE-Block zusammenbauen
708 for(my $y=$x+1;$y<@splitted;$y++) {
709 $else_block .= '{ELSE}'.$splitted[$y];
713 $if_block = substr($if_block,0,length($if_block)-length($else_block));
714 $else_block = (length($else_block) > 6) ?
substr($else_block,6) : ''; # Ansonsten gibt es Fehler
721 my $replacement = ($state) ?
$if_block : $else_block;
723 $template = str_replace
($block,$replacement,$template);
726 $self->set_template($template);
728 # Evtl. verneinte Form parsen
731 $self->parse_if_block('!'.$name,not($state),1);
737 # IF-Bloecke zu allen definierten Variablen verarbeiten
739 # Parameter: Array mit zu verarbeitenden IF-Bloecken (optional)
741 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
743 sub parse_if_blocks
(;@
) {
744 my ($self,@valid) = @_;
746 my @valid_vars = (@valid) ?
@valid : $self->get_var;
748 foreach my $valid_var(@valid_vars) {
749 $self->parse_if_block($valid_var,$self->get_var($valid_var));
753 # parse_trim_blocks()
755 # {TRIM}-Bloecke parsen
757 # Dieser Parser ist nicht rekursiv, was auch nicht
758 # noetig sein sollte.
760 # Parameter: -nichts-
762 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
764 sub parse_trim_blocks
{
767 my $template = $self->get_template;
768 return if(index($template,'{TRIM}') == -1);
772 while((my $begin = index($template,'{TRIM}')) >= 0) {
773 if((my $end = index($template,'{ENDTRIM}',$begin+6)) >= 0) {
774 my $block = substr($template,$begin,$end+9-$begin);
775 my $content = substr($block,6,-9);
777 my $trimmed = $content;
778 $trimmed =~ s/^\s+//s;
779 $trimmed =~ s/\s+$//s;
781 $template = str_replace
($block,$trimmed,$template);
783 $offset = $begin+length($trimmed);
790 $self->set_template($template);
795 # Bedingungstags in einem Vorlagentext verarbeiten
797 # Parameter: 1. Tagname
798 # 2. Status-Code (true => Tag-Inhalt anzeigen
799 # false => Tag-Inhalt nicht anzeigen
801 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
803 sub parse_condtag
($$) {
804 my ($self,$condtag,$state) = @_;
806 my $template = $self->get_template;
808 while(index($template,'<'.$condtag.'>') >= 0) {
809 my $start = index($template,'<'.$condtag.'>'); # Beginn des Blocks
810 my $end = index($template,'</'.$condtag.'>')+length($condtag)+3; # Ende des Blocks
812 my $extract = substr($template,$start,$end-$start); # Kompletten Bedingungsblock extrahieren...
814 my $replacement = ($state) ?
substr($extract,length($condtag)+2,0-length($condtag)-3) : '';
816 $template = str_replace
($extract,$replacement,$template); # Block durch neue Daten ersetzen
819 $self->set_template($template);
824 # {INCLUDE}-Anweisungen verarbeiten
826 # Parameter: -nichts-
828 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
833 my $template = $self->get_template;
834 return if(index($template,'{INCLUDE ') == -1);
840 while((my $begin = index($template,'{INCLUDE ',$offset)) != -1) {
843 my $start = $begin+9;
847 if(substr($template,$start,1) eq '"') {
855 for(my $x=$start;$x<length($template);$x++) {
856 my $c = substr($template,$x,1);
858 if($c eq "\012" && $c eq "\015") {
862 elsif($long == 0 && $c eq ' ') {
866 elsif($long == 1 && $c eq '"') {
867 $skip = 1 if(substr($template,$x+1,1) ne '}');
870 elsif($long == 0 && $c eq '}') {
881 my $filepath = $file;
883 unless(File
::Spec
->file_name_is_absolute($file)) {
884 my $dir = (File
::Spec
->splitpath($self->{'file'}))[1];
885 $dir = '.' unless($dir);
886 $filepath = File
::Spec
->catfile($dir,$file);
890 my $inc = new Template
;
891 $inc->read_file($filepath);
893 my $end = ($long == 1)
894 ?
$start + length($file) + 2
895 : $start + length($file) + 1;
897 my $pre = substr($template,0,$begin);
898 my $post = substr($template,$end);
900 $template = $pre.$inc->get_template.$post;
901 $offset = length($pre)+length($inc->get_template);
908 $self->set_template($template);
917 # Eine Zeichenkette ohne regulaere Ausdruecke auftrennen
918 # (split() hat einen Bug, deswegen verwende ich es nicht)
920 # Parameter: 1. Trennzeichenkette
921 # 2. Zeichenkette, die aufgetrennt werden soll
922 # 3. Maximale Zahl von Teilen
924 # Rueckgabe: Aufgetrennte Zeichenkette (Array)
927 my ($separator,$string,$limit) = @_;
932 my $sep_len = length($separator);
934 while((my $pos = index($string,$separator,$offset)) >= 0 && (!$limit || $x < $limit)) {
935 my $part = substr($string,$offset,$pos-$offset);
936 push(@splitted,$part);
938 $offset = $pos+$sep_len;
943 push(@splitted,substr($string,$offset,length($string)-$offset));
950 # Zeichenkette durch andere ersetzen
952 # Parameter: 1. Zu ersetzender Text
953 # 2. Ersetzender Text
954 # 3. Zeichenkette, in der ersetzt werden soll
956 # Rueckgabe: Bearbeitete Zeichenkette (String)
958 sub str_replace
($$$) {
959 my ($search,$replace,$subject) = @_;
960 $search = quotemeta($search);
962 $subject =~ s/$search/$replace/gs;
969 # Zaehlt, wie oft ein String in einem String vorkommt
970 # (Emulation der PHP-Funktion substr_count())
972 # Parameter: 1. Zu durchsuchender String
973 # 2. Zu suchender String
975 # Rueckgabe: Anzahl der Vorkommnisse (Integer)
977 sub substr_count
($$) {
978 my ($haystack,$needle) = @_;
979 my $qmneedle = quotemeta($needle);
983 $count++ while($haystack =~ /$qmneedle/g);
988 # it's true, baby ;-)
patrick-canterino.de