]>
git.p6c8.net - devedit.git/blob - modules/Template.pm
4 # Template (Version 2.0)
6 # Klasse zum Parsen von Templates
8 # Autor: Patrick Canterino <patrick@patshaping.de>
9 # Letzte Aenderung: 31.7.2006
23 # Rueckgabe: Template-Objekt
28 my $self = {file
=> '', template
=> '', original
=> '', vars
=> {}, defined_vars
=> [], loop_vars
=> {}};
29 return bless($self,$class);
34 # Kompletten Vorlagentext zurueckgeben
38 # Rueckgabe: Kompletter Vorlagentext (String)
42 return shift->{'template'};
47 # Kompletten Vorlagentext aendern
49 # Parameter: Vorlagentext
51 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
55 my ($self,$template) = @_;
56 $self->{'template'} = $template;
61 # Vorlagentext ans Template-Objekt anhaengen
63 # Parameter: Vorlagentext
65 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
69 my ($self,$text) = @_;
70 $self->set_template($self->get_template.$text);
75 # Einlesen einer Vorlagendatei und {INCLUDE}-Anweisungen ggf. verarbeiten
76 # (Text wird an bereits vorhandenen Text angehaengt)
78 # Parameter: 1. Datei zum Einlesen
79 # 2. Status-Code (Boolean):
80 # true => {INCLUDE}-Anweisungen nicht verarbeiten
81 # false => {INCLUDE}-Anweisungen verarbeiten (Standard)
83 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
87 my ($self,$file,$not_include) = @_;
90 $self->{'file'} = $file;
92 open(FILE
,'<'.$file) or croak
"Open $file: $!";
93 read(FILE
, my $content, -s
$file);
94 close(FILE
) or croak
"Closing $file: $!";
96 $self->add_text($content);
99 $self->parse_includes unless($not_include);
104 # Wert einer Variable setzen
106 # Parameter: 1. Name der Variable
107 # 2. Wert, den die Variable erhalten soll
109 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
113 my ($self,$var,$content) = @_;
114 $self->{'vars'}->{$var} = $content;
119 # Wert einer Variable zurueckgeben
121 # Parameter: (optional) Variablenname
123 # Rueckgabe: Wert der Variable;
124 # wenn die Variable nicht existiert, false;
125 # wenn kein Variablenname angegeben wurde, wird ein
126 # Array mit den Variablennamen zurueckgegeben
130 my ($self,$var) = @_;
134 if($self->{'vars'}->{$var})
136 return $self->{'vars'}->{$var};
145 return keys %{$self->{'vars'}};
151 # Daten fuer eine Schleife setzen
153 # Parameter: 1. Name der Schleife
154 # 2. Array-Referenz mit den Hash-Referenzen mit
155 # den Variablen fuer die Schleifendurchgaenge
157 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
159 sub set_loop_data
($$)
161 my ($self,$loop,$data) = @_;
162 $self->{'loop_vars'}->{$loop} = $data;
167 # Daten fuer einen Schleifendurchgang hinzufuegen
169 # Parameter: 1. Name der Schleife
170 # 2. Hash-Referenz mit den Variablen fuer den
173 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
175 sub add_loop_data
($$)
177 my ($self,$loop,$data) = @_;
179 if($self->{'loop_vars'}->{$loop} && ref($self->{'loop_vars'}->{$loop}) eq 'ARRAY')
181 push(@
{$self->{'loop_vars'}->{$loop}},$data);
185 $self->{'loop_vars'}->{$loop} = [$data];
191 # In der Template definierte Variablen auslesen, Variablen
192 # ersetzen, {IF}- und {TRIM}-Bloecke parsen
194 # Parameter: -nichts-
196 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
202 # Zuerst die Schleifen parsen
204 if($self->{'loop_vars'} && (my @loops = keys(%{$self->{'loop_vars'}})))
206 foreach my $loop(@loops)
208 $self->parse_loop($loop);
212 # Normale Variablen durchgehen
214 foreach my $var($self->get_var)
216 my $val = $self->get_var($var);
218 $self->parse_if_block($var,$val);
220 if(ref($val) eq 'ARRAY')
222 $self->fillin_array($var,$val);
226 $self->fillin($var,$val);
230 # Jetzt dasselbe mit denen, die direkt in der Template-Datei definiert
231 # sind, machen. Ich weiss, dass das eine ziemlich unsaubere Loesung ist,
232 # aber es funktioniert
234 $self->get_defined_vars;
236 foreach my $var(@
{$self->{'defined_vars'}})
238 my $val = $self->get_var($var);
240 $self->parse_if_block($var,$val);
241 $self->fillin($var,$val);
244 # {TRIM}-Bloecke entfernen
246 $self->parse_trim_blocks;
251 # Variablen durch Text ersetzen
253 # Parameter: 1. Variable zum Ersetzen
254 # 2. Text, durch den die Variable ersetzt werden soll
256 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
260 my ($self,$var,$text) = @_;
262 $text = '' unless defined $text; # Um Fehler zu vermeiden
264 my $template = $self->get_template;
265 $template = str_replace
('{'.$var.'}',$text,$template);
267 $self->set_template($template);
272 # Variable durch Array ersetzen
274 # Parameter: 1. Variable zum Ersetzen
275 # 2. Array-Referenz, durch die die Variable ersetzt werden soll
276 # 3. Zeichenkette, mit der das Array verbunden werden soll
279 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
281 sub fillin_array
($$;$)
283 my ($self,$var,$array,$glue) = @_;
284 $glue = '' unless defined $glue;
286 $self->fillin($var,join($glue,@
$array));
291 # Template in Datei schreiben
293 # Parameter: Datei-Handle
295 # Rueckgabe: Status-Code (Boolean)
299 my ($self,$handle) = @_;
300 return print $handle $self->get_template;
305 # Den gesicherten Stand des Template-Textes sichern
307 # Parameter: -nichts-
309 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
314 $self->{'template'} = $self->{'original'};
319 # Aktuellen Stand des Template-Textes sichern
320 # (alte Sicherung wird ueberschrieben)
322 # Parameter: -nichts-
324 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
329 $self->{'original'} = $self->{'template'};
334 # Eine Schleife parsen
336 # Parameter: Name der Schleife
338 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
342 my ($self,$name) = @_;
344 my $template = $self->get_template;
345 return if(index($template,'{LOOP '.$name.'}') == -1);
348 my $name_len = length($name);
350 while((my $begin = index($template,'{LOOP '.$name.'}',$offset)) != -1)
352 if((my $end = index($template,'{ENDLOOP}',$begin+6+$name_len)) != -1)
354 my $block = substr($template,$begin,$end+9-$begin);
355 my $content = substr($block,$name_len+7,-9);
357 my $parsed_block = '';
359 for(my $x=0;$x<scalar @
{$self->{'loop_vars'}->{$name}};$x++)
361 my $loop_data = $self->{'loop_vars'}->{$name}->[$x];
362 my @loop_vars = keys(%$loop_data);
364 my $ctpl = new Template
;
365 $ctpl->set_template($content);
367 foreach my $loop_var(@loop_vars)
369 $ctpl->set_var($name.'.'.$loop_var,$loop_data->{$loop_var});
373 $parsed_block .= $ctpl->get_template;
378 $template = str_replace
($block,$parsed_block,$template);
379 $offset = $begin+length($parsed_block);
387 $self->set_template($template);
392 # In der Template-Datei definierte Variablen auslesen
394 # Parameter: -nichts-
396 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
402 my $template = $self->get_template;
403 return if(index($template,'{DEFINE ') == -1);
407 while(index($template,'{DEFINE ',$offset) != -1)
409 my $begin = index($template,'{DEFINE ',$offset)+8;
417 my $define_block = 0;
419 for(my $x=$begin;$x<length($template);$x++)
421 if(substr($template,$x,1) eq "\012" || substr($template,$x,1) eq "\015")
423 # Wenn in einem {DEFINE}-Block ein Zeilenumbruch gefunden wird,
424 # brechen wir mit dem Parsen des Blockes ab
431 if(substr($template,$x,1) eq '"')
433 # Der Inhalt der Variable ist hier zu Ende
437 if(substr($template,$x+1,1) eq '}')
439 # Hier ist der Block zu Ende
441 if(not defined $self->get_var($name))
443 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
445 $self->set_var($name,$content);
446 push(@
{$self->{'defined_vars'}},$name);
449 # {DEFINE}-Block entfernen
451 my $pre = substr($template,0,$begin-8);
452 my $post = substr($template,$x+2);
454 $template = $pre.$post;
458 $offset = length($pre);
462 elsif(substr($template,$x,1) eq '\\')
464 # Ein Backslash wurde gefunden, er dient zum Escapen von Zeichen
466 if(substr($template,$x+1,1) eq 'n')
468 # "\n" in Zeilenumbrueche umwandeln
474 $content .= substr($template,$x+1,1);
481 $content .= substr($template,$x,1);
490 if(substr($template,$x,1) eq '"')
502 # Variablennamen auslesen
504 if(substr($template,$x,1) eq '}' && $name ne '')
506 # Wir haben einen {DEFINE}-Block
511 # Alles ab hier sollte mit dem Teil verbunden werden, der das
512 # {DEFINE} in einer Zeile verarbeitet
514 # Der Parser fuer {DEFINE}-Bloecke ist nicht rekursiv, was auch
515 # nicht noetig sein sollte
517 if((my $end = index($template,'{ENDDEFINE}',$x)) != -1)
521 $content = substr($template,$x,$end-$x);
523 if(not defined $self->get_var($name))
525 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
527 $self->set_var($name,$content);
528 push(@
{$self->{'defined_vars'}},$name);
531 my $pre = substr($template,0,$begin-8);
532 my $post = substr($template,$end+11);
534 $template = $pre.$post;
538 $offset = length($pre);
546 elsif(substr($template,$x,1) ne ' ')
548 $name .= substr($template,$x,1);
550 elsif(substr($template,$x,1) ne '')
563 $self->set_template($template);
568 # IF-Bloecke verarbeiten
570 # Parameter: 1. Name des IF-Blocks (das, was nach dem IF steht)
571 # 2. Status-Code (true => Inhalt anzeigen
572 # false => Inhalt nicht anzeigen
573 # 3. true => Verneinten Block nicht parsen
574 # false => Verneinten Block parsen (Standard)
576 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
578 sub parse_if_block
($$;$)
580 my ($self,$name,$state,$no_negate) = @_;
581 my $template = $self->get_template;
585 while(index($template,'{IF '.$name.'}') >= 0)
587 # Das alles hier ist nicht wirklich elegant geloest...
588 # ... aber solange es funktioniert... ;-)
592 my $start = index($template,'{IF '.$name.'}');
593 my $tpl_tmp = substr($template,$start);
594 my @splitted = explode
('{ENDIF}',$tpl_tmp);
596 my $block = ''; # Kompletter bedingter Block
597 my $ifs = 0; # IF-Zaehler (wird fuer jedes IF erhoeht und fuer jedes ENDIF erniedrigt)
601 for(my $x=0;$x<@splitted;$x++)
603 croak
'Nesting error found while parsing IF block "'.$name.'" nr. '.$count.' in template file "'.$self->{'file'}.'"' if($x == $#splitted);
605 $ifs += substr_count
($splitted[$x],'{IF '); # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
606 $ifs--; # Zaehler um 1 erniedrigen
607 $block .= $splitted[$x].'{ENDIF}'; # Daten zum Block hinzufuegen
611 # Zaehler wieder 0, also haben wir das Ende des IF-Blocks gefunden :-))
617 my $if_block = substr($block,length($name)+5,-7); # Alles zwischen {IF} und {ENDIF}
621 my $else_block = ''; # Alles ab {ELSE}
622 $ifs = 0; # IF-Zaehler
624 @splitted = explode
('{ELSE}',$if_block);
626 for(my $x=0;$x<@splitted;$x++)
628 $ifs += substr_count
($splitted[$x],'{IF '); # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
629 $ifs -= substr_count
($splitted[$x],'{ENDIF}'); # Vom Zaehler jedes Vorkommen von ENDIF abziehen
633 # Zaehler 0, also haben wir das Ende des IF-Abschnitts gefunden
635 # Aus dem Rest den ELSE-Block zusammenbauen
637 for(my $y=$x+1;$y<@splitted;$y++)
639 $else_block .= '{ELSE}'.$splitted[$y];
644 $if_block = substr($if_block,0,length($if_block)-length($else_block));
645 $else_block = (length($else_block) > 6) ?
substr($else_block,6) : ''; # Ansonsten gibt es Fehler
652 my $replacement = ($state) ?
$if_block : $else_block;
654 $template = str_replace
($block,$replacement,$template);
657 $self->set_template($template);
659 # Evtl. verneinte Form parsen
663 $self->parse_if_block('!'.$name,not($state),1);
667 # parse_trim_blocks()
669 # {TRIM}-Bloecke parsen
671 # Dieser Parser ist nicht rekursiv, was auch nicht
672 # noetig sein sollte.
674 # Parameter: -nichts-
676 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
678 sub parse_trim_blocks
682 my $template = $self->get_template;
683 return if(index($template,'{TRIM}') == -1);
687 while((my $begin = index($template,'{TRIM}')) >= 0)
689 if((my $end = index($template,'{ENDTRIM}',$begin+6)) >= 0)
691 my $block = substr($template,$begin,$end+9-$begin);
692 my $content = substr($block,6,-9);
694 my $trimmed = $content;
695 $trimmed =~ s/^\s+//s;
696 $trimmed =~ s/\s+$//s;
698 $template = str_replace
($block,$content,$template);
700 $offset = $begin+length($trimmed);
708 $self->set_template($template);
713 # Bedingungstags in einem Vorlagentext verarbeiten
715 # Parameter: 1. Tagname
716 # 2. Status-Code (true => Tag-Inhalt anzeigen
717 # false => Tag-Inhalt nicht anzeigen
719 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
721 sub parse_condtag
($$)
723 my ($self,$condtag,$state) = @_;
725 my $template = $self->get_template;
727 while(index($template,'<'.$condtag.'>') >= 0)
729 my $start = index($template,'<'.$condtag.'>'); # Beginn des Blocks
730 my $end = index($template,'</'.$condtag.'>')+length($condtag)+3; # Ende des Blocks
732 my $extract = substr($template,$start,$end-$start); # Kompletten Bedingungsblock extrahieren...
734 my $replacement = ($state) ?
substr($extract,length($condtag)+2,0-length($condtag)-3) : '';
736 $template = str_replace
($extract,$replacement,$template); # Block durch neue Daten ersetzen
738 $self->set_template($template);
743 # {INCLUDE}-Anweisungen verarbeiten
745 # Parameter: -nichts-
747 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
753 my $template = $self->get_template;
754 return if(index($template,'{INCLUDE ') == -1);
760 while((my $begin = index($template,'{INCLUDE ',$offset)) != -1)
764 my $start = $begin+9;
768 if(substr($template,$start,1) eq '"')
777 for(my $x=$start;$x<length($template);$x++)
779 my $c = substr($template,$x,1);
781 if($c eq "\012" && $c eq "\015")
786 elsif($long == 0 && $c eq ' ')
791 elsif($long == 1 && $c eq '"')
793 $skip = 1 if(substr($template,$x+1,1) ne '}');
796 elsif($long == 0 && $c eq '}')
810 my $filepath = $file;
812 unless(File
::Spec
->file_name_is_absolute($file))
814 my $dir = (File
::Spec
->splitpath($self->{'file'}))[1];
815 $dir = '.' unless($dir);
816 $filepath = File
::Spec
->catfile($dir,$file);
821 my $inc = new Template
;
822 $inc->read_file($filepath);
824 my $end = ($long == 1)
825 ?
$start + length($file) + 2
826 : $start + length($file) + 1;
828 my $pre = substr($template,0,$begin);
829 my $post = substr($template,$end);
831 $template = $pre.$inc->get_template.$post;
832 $offset = length($pre)+length($inc->get_template);
839 $self->set_template($template);
848 # Eine Zeichenkette ohne regulaere Ausdruecke auftrennen
849 # (split() hat einen Bug, deswegen verwende ich es nicht)
851 # Parameter: 1. Trennzeichenkette
852 # 2. Zeichenkette, die aufgetrennt werden soll
853 # 3. Maximale Zahl von Teilen
855 # Rueckgabe: Aufgetrennte Zeichenkette (Array)
859 my ($separator,$string,$limit) = @_;
864 my $sep_len = length($separator);
866 while((my $pos = index($string,$separator,$offset)) >= 0 && (!$limit || $x < $limit))
868 my $part = substr($string,$offset,$pos-$offset);
869 push(@splitted,$part);
871 $offset = $pos+$sep_len;
876 push(@splitted,substr($string,$offset,length($string)-$offset));
883 # Zeichenkette durch andere ersetzen
885 # Parameter: 1. Zu ersetzender Text
886 # 2. Ersetzender Text
887 # 3. Zeichenkette, in der ersetzt werden soll
889 # Rueckgabe: Bearbeitete Zeichenkette (String)
893 my ($search,$replace,$subject) = @_;
894 $search = quotemeta($search);
896 $subject =~ s/$search/$replace/gs;
903 # Zaehlt, wie oft ein String in einem String vorkommt
904 # (Emulation der PHP-Funktion substr_count())
906 # Parameter: 1. Zu durchsuchender String
907 # 2. Zu suchender String
909 # Rueckgabe: Anzahl der Vorkommnisse (Integer)
913 my ($haystack,$needle) = @_;
914 my $qmneedle = quotemeta($needle);
918 $count++ while($haystack =~ /$qmneedle/g);
923 # it's true, baby ;-)
patrick-canterino.de