Archiv verlassen und diese Seite im Standarddesign anzeigen : Template Klasse - Rekursiv Pattern
Hallo zusammen,
Ich habe vor, mir für ein Projekt eine Template Klasse zu schreiben.
Ich brauche kaum extra Funktionen, im Grunde hat mir eval() immer für Templates gereicht.
Von eval() will ich jetzt aber mal weg, zumal viele sagen, "eval ist evil".
Die Klasse muss folgende Funktionen enthalten:
- Variablen bzw. strings ersetzen.
(- Eine Schleifenfunktion -- Ist nicht unbedingt nötig)
- Template ausgeben
Außerdem sollte sie mit Templates aus verschiedenen Verzeichnissen klarkommen.
Das wars schon soweit. :)
An sich kein Problem, ich poste hier allerdings, weil die Klasse möglichst schnell und unkompliziert funktionieren soll.
Der Punkt des Ersetzen von Strings ist mir ein Dorn im Auge, zumal sich das Problem ja scheinbar nur mit str_replace lösen lässt. Ich habe mir überlegt, ob es da vielleicht noch bessere Möglichkeiten gibt, zumal die Funktion sicher nicht die schnellste sein dürfte.
Wie ist es denn mit einer Verwaltung der Templates per Datenbank, also einer Art cache Funktion?
Wie könnte ich das Problem lösen, eine möglichst schnelle und einfache Template Klasse zu basteln?
Es wär nett, wenn ihr ein paar Denkanstöße geben könntet.
Mfg Oli
hmm,
um str_replace wirst du wohl nicht drumherum kommen.
eine schleifenfunktion sollte meiner meinung nach auf jeden fall rein, bzw. das parsen von ganzen blöcken ... dafür wirst du wohl preg_replace nehmen müssen.
zum thema cache: du speicherst das erstellte tpl im filesystem, und aktualisierst es nur, wenn es nötig ist. speicherst einfach noch eine datei, wo drin steht, ob das tpl neu aufzubauen ist ... (was dümmeres ist mir zumindest zu diesem thema nicht eingefallen)
(- Eine Schleifenfunktion -- Ist nicht unbedingt nötig)
schwates! und wie die nötig ist. das ist mit das effektivste einer tpl-class. sollte sogar verschachtelungen zulassen.
guckst du hier (http://php-resource.de/forum/showthread.php?s=&postid=301317#post301317)
viel spaß damit.
ach ja: API ist wie die von smarty.
irgendwo fliegt noch ne php5 portierung rum. müsste ich suchen wenn bedarf ist.
Hm, danke erstmal für die Antworten.
@TobiaZ: Eine Schleifenfunktion habe ich an sich nie gebraucht, ich hab die Schleifen immer im Script durchlaufen lassen und einfach das Template erweitert... Da sind Verschachtelungen natürlich auch möglich gewesen.
Das Problem ist eben bei einer schleifenfunktion, dass man z.B. mit den Strings die aus einer Datenbank kommen nichts mehr anstellen kann.
@rythms: Die Klasse sieht ganz ok aus, aber ich würd gern was eigenes machen. Was genau verstehst du denn unter dem Ersetzen einer Methode? (Methoden hab ich bisher nur in Bezug auf Klassen gehört...)
@derHund: "Das Parsen von Blöcken"? Wie meinst du das? (Mit Blöcken meinst du kommentierte Blöcke im HTML oder?)
Ich bin noch recht neu auf dem Gebiet, verzeiht mir die Fragen.
MfG Oli
ich hab die Schleifen immer im Script durchlaufen lassen und einfach das Template erweitert. Dass ist unsinnig. Templates dienen dazu den HTML-Code von Programmcode zu trennen, und was machst du, du schmeißt es noch zudammen. Aber das ist noch nicht genug. Du verteils den HTML-Code zusätzlich auf mehrere Dateien, so dass man den absoluten Wirrwar hat.
Das Problem ist eben bei einer schleifenfunktion, dass man z.B. mit den Strings die aus einer Datenbank kommen nichts mehr anstellen kann. Das kann ich nicht nachvollziehen.
@derHund: "Das Parsen von Blöcken"? Wie meinst du das? (Mit Blöcken meinst du kommentierte Blöcke im HTML oder?) Wohl eher die Schleifenfunktion!
Hm, ich sagte doch ich bin recht neu auf dem Gebiet... :(
Also ich hatte Schleifenbits immer in extra Dateien, das stimmt. War auch sehr unübersichtlich.
Das Problem ist eben bei einer schleifenfunktion, dass man z.B. mit den Strings die aus einer Datenbank kommen nichts mehr anstellen kann.
Ich meine sowas:
for($i=0;$i<count($arr);$i++) {
$arr[$i] = trim($arr[$i]);
}
Hier kann ich den String oder was auch immer noch mit trim() bearbeiten, wenn ich das array gleich an die klasse weitergebe, hab ich die Möglichkeit nicht, es sei denn ich lasse es vorher im script machen. Das läuft darauf hinaus, dass ich nachher 2 Schleifen habe, einmal die, die alle Elemente mit trim() bearbeitet, im Script und einmal die in der Template Klasse, die das array schließlich mit dem Template verarbeitet.
, wenn ich das array gleich an die klasse weitergebe, hab ich die Möglichkeit nicht, es sei denn ich lasse es vorher im script machen. Wo willst du es denn sonst machen, wenn nicht im Script??? Bei ordentlichen Programmen ist es meist so, dass du ohnehin erst deine strings bearbeiten musst, und es nicht 1:1 auf der seite ausgeben kannst. Alleine schon bei sachen wie specialchars, etc.
Das läuft darauf hinaus, dass ich nachher 2 Schleifen habe, einmal die, die alle Elemente mit trim() bearbeitet, im Script und einmal die in der Template Klasse, die das array schließlich mit dem Template verarbeitet. Ja und??? dafür ist die Klasse schließlich dar. Sehe dein Problem also nicht. :dontknow:
2 schleifen dauern länger als eine. :)
Aber ok, dann nehm ich das mal so hin.
Kann mir nochmal jemand erklären, was genau derHund mit seinen Blöcken meint?
Ein Block ist das Stückchen im Template, welche durch die Schleifenfunktion immer wieder wiederholt wird. Das was du in extra Dateien hast.
hmm,
mit blöcken meine ich bereiche im script, die entweder mehrmals oder bedingungs-abhängig geparst werden ... z.b.
<tbody>
{{BEGIN:SENTENCES}}
<tr class="{{CLASS}}">
<td>{{SENTENCE}}</td>
<td>{{COUNT}}</td>
</tr>
{{END:SENTENCES}}
</tbody>
das feature ist elementar, ich wüßte nicht, wie du sonst sich wiederholende sachen parsen willst ...
der block wird 'ausgewählt', und dann in einer schleife mehrmals geparsed, durchlaufen, ... um dann z.b. <tbody>
<tr class="odd">
<td>puzzle generator</td>
<td>3</td>
</tr>
<tr class="even">
<td>flash "puzzle generator" -crossword</td>
<td>1</td>
</tr>
<tr class="odd">
<td>noothertemplate</td>
<td>1</td>
</tr>
</tbody>
zu erhalten ... weißt?
ein feature, was die class auf jeden fall enthalten sollte.
das feature ist elementar, ich wüßte nicht, wie du sonst sich wiederholende sachen parsen willst ... Hat er doch gesagt, er knallt das alles hart in denPHP-Code rein. :)
Weil das ja viiiel schneller ist, als eine schleife bem zusammenstellen des Templates ;)
Original geschrieben von TobiaZ
Hat er doch gesagt, er knallt das alles hart in denPHP-Code rein. :)
Weil das ja viiiel schneller ist, als eine schleife bem zusammenstellen des Templates ;)
Jaja, macht euch nur lustig... :(
Ich mach mich mal an die Klasse.
/Edit:
So, ich hab auf ganzer Linie versagt. :)
Meine bisherige Klasse:
<?php
/*
* Template Class
*/
class template {
var $open; // Starttag der Platzhalter
var $close; // Endtag der Platzhalter
var $values; // Werte der zu ersetzenden Strings
var $tplF; // Template Verzeichnis
var $fileType; // Endung der Templates (.tpl, .htm o.ä.)
// Definieren von Variablen
function template($open='{', $close='}', $fileType='.tpl', $tplF='./') {
$this->open = $open;
$this->close = $close;
$this->values = array();
$this->fileType = $fileType;
}
// Den Platzhaltern Werte zuweisen
// Wenn isFile = true, wird versucht eine Datei als Wert einzubinden
function assign($var, $value, $isFile=false, $tplFolder=$this->tplF) {
if($isFile == true) {
if(!is_readable($tplFolder.$value.$this->fileType)) {
// ERROR
} else {
$this->values[$var] = file_get_contents($tplFolder.$value.$this->fileType);
}
} else {
$this->values[$var] = $value;
}
}
// Ausgabe der Templates
function output($tpl) {
if(!is_readable($this->tplF.$tpl.$this->fileType)) {
// ERROR
} else {
$content = file_get_contents($this->tplF.$tpl.$this->fileType);
echo $this->parse($content);
}
}
// Parsen
function parse($string) {
}
}
?>
Die Frage ist jetzt, wie ich die parse Funktion gestalte. Klar ist mir folgendes:
Ich muss das Template nach {Platzhaltern} durchsuchen. Außerdem können Schleifen ( {loop}{/loop} ) vorkommen.
Ich kann mir aber irgendwie nicht vorstellen, wie das mit den Schleifen laufen soll.
Mal angenommen, ich hab im auszuführenden Script ne SQL query, verarbeite das ganze und hab folgendes array:
Array
(
[1] => Array
(
[name] => Peter
[nachname] => Mueller
[Wohnort] => Berlin
)
[2] => Array
(
[name] => Klaus
[nachname] => Raus
[Wohnort] => Hamburg
)
[3] => Array
(
[name] => Hans
[nachname] => imGlueck
[Wohnort] => Hoelle
)
)
Ich hab irgendwie gar keine Vorstellung, was ich jetzt im Script und im Template tun muss, damit er mir alle drei Datensätze anzeigt, zumal ich beim Erstellen des Templates ja keine Ahnung habe, wie viele Datensätze aus der Tabelle geholt wurden. :dontknow:
Kann mir das vielleicht jemand an einem Beispiel erklären? Den anderen Template Klassen kann ich das nicht entnehmen, wie die mit Schleifen umgehen. :(
MfG Oli
Nach weiteren Stunden probieren hab ichs immer noch nicht so richtig hinbekommen.
Klar ist mir jetzt, dass ich zum Parsen einmal mit nem normalen preg_replace die Platzhalter ersetzen muss, und mit preg_replace_callback Schleifen bearbeiten muss. Der Inhalt der Schleifen wird dann an eine Funktion übergeben, die diesen dann eben mehrmals zurückgibt.
Schleierhaft ist mir nur, wie ich das mit dem Script nachher in Verbindung bringen soll. Woher weiß diese Funktion zum Beispiel, wie oft die Schleife durchgeführt werden soll?
An verschachtelte Arrays mag ich gar nicht erst denken, geschweige denn an verschachtelte Blöcke.
Wär nett wenn ihr mir weiterhelfen würdet,
MfG Oli
Ich verstehe nicht, warum du nicht meine Klasse benutzen willst wenn du fast identisch das gleiche baust?
MaxP0W3R 13-10-2004, 16:39 es geht auch um den lerneffekt beim schreiben, daher werde ich auch meine eigene template klasse schreiben...
schlimmerfinger 13-10-2004, 16:41 Original geschrieben von rythms
Ich verstehe nicht, warum du nicht meine Klasse benutzen willst wenn du fast identisch das gleiche baust? Warum werden überhaupt noch Template Enigines gebaut... Es gibt alleine in Pear schon drei und zig andere...
Original geschrieben von rythms
Ich verstehe nicht, warum du nicht meine Klasse benutzen willst wenn du fast identisch das gleiche baust?
Weil ich einige Funktionen darin nicht brauche, dafür welche brauche, die nicht in deiner Klasse sind.
Und ich will da nicht drin rumwerkeln.
Außerdem würde ich wie schon gesagt wurde, gerne dazulernen.
@schlimmerfinger: Das könntest du in vielen threads hier im Forum sagen. Ich habe Spass beim erstellen von Klassen etc. und kann nachher auch sagen, dass ich die gemacht habe. Ob es nun schon welche gibt ist doch egal. Wie viele Gästebücher, Foren, CM Systeme gibt es schon, und trotzdem werden dauernd neue geschrieben.
aber es scpricht nichts dagegen, wenn du versuchst die block-funktion von irgend einer klasse nachzuvollziehen. das ganze nochmals erklären wird dir vermutlich niemand.
nu soviel
preg_match zum suchen der platzhalter (egal ob str oder block)
preg_replace zum umwandeln der blocks in normale platzhalter
str_replace zum ersetzen
schlimmerfinger 13-10-2004, 16:58 @Oli Warum nimmst du dann nicht die Engine von rythms und erweiterst sie dann. Es hat den gleichen lerneffeckt, wenn nicht noch ein grösseren.
@Oli Warum nimmst du dann nicht die Engine von rythms und erweiterst sie dann. Es hat den gleichen lerneffeckt, wenn nicht noch ein grösseren. da wäre ich mir aber auch nicht sicher. jetzt lasst ihn doch einfach machen...
ich hab auch meine eigene klasse, die im gegensatz zu den meisten anderen das template erst parst, wenn es nötig ist.
Original geschrieben von schlimmerfinger
@Oli Warum nimmst du dann nicht die Engine von rythms und erweiterst sie dann. Es hat den gleichen lerneffeckt, wenn nicht noch ein grösseren.
Weil ich diese engine später vielleicht einmal für irgendwelche scripte nutzen will, die ich auch zum Download anbiete. Da mag ich keine fremden copyrights, und rausnehmen will ich die natürlich auch nicht.
@TobiaZ: Hm, ja, inzwischen hab ich es glaub ich verstanden.
Weil ich unntötige Funktionen sparen will, hab ich es mit create_function versucht:
$values = $this->values;
$pattern = '°'.preg_quote($this->open,'°').'(.*)'.preg_quote($this->close,'°').'°i';
$string = preg_replace_callback($pattern,create_function('$key','{global $values; return $values[$key];}'),$string);
Leider gibt die kreierte Funktion nichts zurück... :/ (Lasse ich die { } und das global weg, kommt auch nichts.
wenn die Klasse von rythmus (oder so) zumindest funktionieren würde ... mein Post bei seiner Klasse wird ignorier ... und in ICQ antwortet er auch nicht ...
ich "muss" auch andauernd support für meine scripte geben, und tuhe das auch nicht immer so gerne, aber ich weiss das ich anderen helfe.
Also, bitte melde dich mal bei mir ! (162424599)
das template erst parst, wenn es nötig ist.häh? wieso sollte man parsen, wenn es nicht nötig ist? meinst du wenn - zeitlich, zu dem zeitpunkt - oder falls, falls es nötig ist?
/edit: Oh, dummer Fehler... sry.
Einen augenblick, post folgt gleich. :)
zeitlich natürlich. nehme rücksicht auf den arbeitsspeicher
ok, sorry dass ich euch weiter zur Last falle, aber ich hab grad extreme Probleme mit einem preg_replace.
<?php
$pattern = '/'.preg_quote($this->open,'/').'array name=(.*?)'.preg_quote($this->close,'/').'(.*?)'.preg_quote($this->open,'/').'\/array'.preg_quote($this->close,'/').'/iU';
$string = preg_replace($pattern,'[\\1] UND [\\2]<br>',$string);
?>
So sieht der code aus.
Pattern enthält, wenn ich es ausgeben lasse:
/\{array name=(.*?)\}(.*?)\{\/array\}/iU
Als $string fungiert:
<html><head>
<title>{title}</title>
</head><body>{Inhalt}<br \>
{array name=tier} {name} ist ein {art} {/array}
</html>
Das Ergebnis ist nicht das, was ich erwarte, sondern folgendes:
[tier} ist ein {art] UND [ ]
Ich hab schon mehrere Leute gefragt, aber der pattern müsste einwandfrei sein, den Fehler hat keiner gefunden....
Ihr könnt mir sicher weiterhelfen. :)
MfG Oli
/\{array name=([^\}]?)\}(.*?)\{\/array\}/iU
Das geht nicht, zumal ich den regexp auch nicht so ganz verstehe...
Jetzt trifft das Suchmuster nicht mehr auf den String zu.
Bei mir hats das immerhin...
dann so
/\{array name=([^\}]+)\}(.*?)\{\/array\}/iU
zu testen unter http://tbt.dyndns.org/regtest/regtest.php
/edit2: Ok, ich habs hingekriegt.
Original geschrieben von TheBo
wenn die Klasse von rythmus (oder so) zumindest funktionieren würde ... mein Post bei seiner Klasse wird ignorier ... und in ICQ antwortet er auch nicht ...
ich "muss" auch andauernd support für meine scripte geben, und tuhe das auch nicht immer so gerne, aber ich weiss das ich anderen helfe.
Also, bitte melde dich mal bei mir ! (162424599)
Oh, hab den Post "drüben" nicht gesehen und lese fast nur hier im Brainstorming. ICQ bleibt zwecks Arbeitsmoral aus :)
Schreib doch am besten mal ne email.
So, weiter gehts mit Problemen.
Ich hab die Klasse jetzt auf folgendem Stand:
class template {
var $open; // Starttag der Platzhalter
var $close; // Endtag der Platzhalter
var $clearopen; // Starttag _ohne_ escape!
var $clearclose; // Endtag _ohne_ escape!
var $values; // Werte der zu ersetzenden Strings
var $tplF; // Template Verzeichnis
var $fileType; // Endung der Templates (.tpl, .htm o.ä.)
// Definieren von Variablen
function template($open='{', $close='}', $fileType='.tpl', $tplF='./') {
$this->open = preg_quote($open, '/');
$this->close = preg_quote($close, '/');
$this->clearopen = $open;
$this->clearclose = $close;
$this->values = array();
$this->fileType = $fileType;
$this->tplF = $tplF;
}
// Den Platzhaltern Werte zuweisen
// Wenn isFile = true, wird versucht eine Datei als Wert einzubinden
function assign($var, $value, $isFile=false, $tplFolder=false) {
if($tplFolder == false) $tplFolder = $this->tplF;
if($isFile == true) {
if(!is_readable($tplFolder.$value.$this->fileType)) {
trigger_error('File <b>'.$tplFolder.$value.$this->fileType.'</b> is not readable.',ERROR);
} else {
$this->values[$var] = file_get_contents($tplFolder.$value.$this->fileType);
}
} else {
$this->values[$var] = $value;
}
}
// Ausgabe der Templates
function output($tpl) {
if(!is_readable($this->tplF.$tpl.$this->fileType)) {
trigger_error('File <b>'.$this->tplF.$tpl.$this->fileType.'</b> is not readable.',FATAL);
} else {
$content = file_get_contents($this->tplF.$tpl.$this->fileType);
echo $this->parse($content);
}
}
// replace var
function replace_var($key) {
return $this->values[$key[1]];
}
// Array ersetzen
function replace_array($ar) {
$pattern = '/'.$this->open.'(.*?)'.$this->close.'/iU';
$pattern2 = '/'.$this->open.'array name=([^'.$this->close.']+)'.$this->close.'(.*?)'.$this->open.'\/array'.$this->close.'/iUm';
$return = '';
for($i=0;$i<count($this->values[$ar[1]]);$i++) {
$string = $ar[2];
if(preg_match($pattern2, $string)) $string = $this->parse($string, $only=2);
foreach($this->values[$ar[1]][$i] AS $key => $val) {
$string = str_replace($this->clearopen.$key.$this->clearclose,$val,$string);
}
// $string = $this->parse($string, $only=1);
$return .= $string;
}
return $return;
}
// Parsen
function parse($string, $only=3) {
if($only != 2) {
// Ersetzen von Schleifen
$pattern = '/'.$this->open.'array name=([^'.$this->close.']+)'.$this->close.'(.*?)'.$this->open.'\/array'.$this->close.'/iUm';
$string = preg_replace_callback($pattern,array(&$this, 'replace_array'),$string);
}
if($only != 1) {
// Ersetzen von einfachen Platzhaltern
$pattern = '/'.$this->open.'(.*?)'.$this->close.'/i';
$string = preg_replace_callback($pattern,array(&$this, 'replace_var'),$string);
$string = $this->parse($string, $only=1);
}
return $string;
}
}
Derzeit habe ich noch 2 Probleme:
1. Das Script sucht beim Ersetzen eines Arrays nach dem ersten Vorkommen von {array name=xxx} und nach dem letzten {/array}, egal ob die zusammengehören. Die ? sind im Regexp vorhanden, ich wüsste nicht, wo ich da noch eins zwischenbauen sollte...
2. Wenn zwischen {array name=xxx} und {/array} ein Zeilenumbruch ist, trifft das Suchmuster nicht mehr zu, trotz Modifier "m".
Vielen Dank für Hilfe im Vorraus.
MfG Oli
zu 1. unter den annahme, du benutzt keine verschachtelungen, U
zu 2. m erfüllt andere zwecke, nimm s
habe mir, btw, dein pattern nicht angeschaut ...
U verwende ich bereits als Modifier.
Davon abgesehen, dass Verschachtelungen möglich sein sollen, gehts auch ohne nicht.
s bei Zeilenumbrüchen funktioniert, danke.
http://www.php-resource.de/tutorials/read/10/1/ laut diesem Tutorial ist m dafür aber geeignet, s wäre genau das Gegenteil.
Davon abgesehen, dass Verschachtelungen möglich sein sollen, gehts auch ohne nicht. eventuell solltest du dann mal ein klartext-pattern zeigen ...
für verschachtelungen solltest du dir mal (?R) anschauen ... dient erstmal nur dazu, die richtige verschachtelung zu finden.
#{array name=(.+)}(.+){/array}#isUmatched bei meinen tests auf deine gegebenheit
die { brauchst du scheinbar nicht slashen, weil php das wohl unterscheiden kann ...
hi derHund,
also dein Suchmuster funktioniert soweit.
Das mit dem (?R) hab ich allerdings nicht verstanden.
Ich hab mich mal hier (http://www.php-faq.de/ch/ch-regexp.html) umgesehen, aber da nichts darüber gefunden.
MfG Oli
Ich denke mal, dass der Hund ausschließende Rules(=R) meint. Siehe das Tut auf dieser Seite (von Sky)
Das mit dem (?R) hab ich allerdings nicht verstanden.
http://www.php-resource.de/manual.php?p=pcre.pattern.syntax
ganz unten, vorletzter abschnitt ... damit du den zugehörigen, schließenden tag findest ...
so, da bin ich wieder.
Also so wie ich derHund verstanden habe, muss ich mein Pattern so aufbauen:
Suche nach alle {array name=x} ... {/array}, wo in ... kein weiteres {array name=x} enthalten ist.
Das wäre zumindest die Lösung, da er dann Verschachtelungen von Innen nach außen ersetzt.
Mit den Assertions ist das allerdings ein bisschen komisch.
Mein Suchmuster sieht so aus:
#{array name=(.*?)} (.*) {/array}#isU
Jetzt muss ich es mit diesen Assertions schaffen, dass es nur zutrifft, wenn zwischen den Tags kein weiteres {array name=(.*?)} steht.
#{array name=(.*?)} (.*(?!:{array name=(.*?)})) {/array}#isU
Mein Denkansatz. Funktioniert leider nicht.
Ich hoffe, ich hab dich richtig verstanden, derHund.
MfG Oli
Hallo,
Leider bin ich nach etlich langem rumprobieren immer noch zu keiner Lösung gekommen. Ich fasse mal zusammen, wo ich derzeit stehe:
Ich suche nach wie vor, einen pattern, der in einem templates zeichenketten wie {array name=(.*)} (.*) {/array} ersetzt durch den Rückgabewert einer Funktion.
Mein code:
$pattern = '/'.$this->open.'array'.$add.' name=(.*)'.$this->close.'(.*)'.$this->open.'\/array'.$this->close.'/isU';
$string = preg_replace_callback($pattern,array(&$this, 'replace_array'),$string);
Folgende Probleme habe ich:
Ich komme mit dem greedy nicht so ganz zurecht. Wenn ich einerseits verschachtelte, aber auch aufeinander folgende arrays habe, dann wird logischerweise immer was falsch gemacht. Bsp:
{array name=asdj} ich bin ein verschachtelrtes {array name=zweites} array {/array} und und und {/array} <br> {array name=drittes} Ich stehe hinter den beiden anderen {/array}
Wenn ich den modifier U oder ein ? nicht verwende, dann nimmt der das {array name=xxx} vom allerersten array, und das {/array} vom allerletzten. Die gehören dummerweise nicht zueinander. Wenn ich den modifier benutze, dann nimmt er logischerweise das erste {array name=xxx} und das erste {/array}, die ja auch nicht zueinander gehören. Hier hab ich nur einen Lösungsansatz: Wenn ich den regex so wenig wie möglich fressen lasse, ihm aber sage, dass zwischen den tags kein array name=xxx} vorkommen darf, dann müsste er sich von innen nach außen vorarbeiten können. Wie ich das mache, ist mir aber schleierhaft.
Verschachtelungen sind zwar, das habe ich inzwischen soweit verstanden, mit (?R) also recursive pattern lösbar, aber wie genau das auszusehen hat, weiß ich noch nicht.
Wäre super nett, wenn ihr versucht mir nochmal zu helfen.
Immer bedenken, dass ich php (noch) nicht gut kann.
MfG Oli
Ich suche nach wie vor, einen pattern, der in einem templates zeichenketten wie {array name=(.*)} (.*) {/array} ersetzt durch den Rückgabewert einer Funktion. anderer vorschlag:
lass es einfach durch block_$1 ersetzen.
hab ich auch schon dran gedacht, aber erstmal muss er ja das passende {/array} zum {array name=xxx} finden
hab ich auch schon dran gedacht, aber erstmal muss er ja das passende {/array} zum {array name=xxx} findendas passende findest du mit hilfe von ?R. dazu ist es da.
kuckst du: http://pcre.nophia.de/evaluate/267c6b6b055f60a0c9a5b384be40198b/index.php#output bzw. http://pcre.nophia.de/evaluate/1a7b920311631dd49aa8ad714d825f3f/index.php#output
in \1 findest du den namen, in \2 den inhalt, den du erneut - vorzugsweise rekursiv - parsen mußt. siehe auch das beispiel in den codeschnipseln.
Vielen lieben Dank, alle zusammen.
Die Klasse funktioniert jetzt so, wie sie soll.
Hier nochmal das ganze:
<?php class template {
var $open; // Starttag der Platzhalter
var $close; // Endtag der Platzhalter
var $values; // Werte der zu ersetzenden Strings
var $tplF; // Template Verzeichnis
var $fileType; // Endung der Templates (.tpl, .htm o.ä.)
var $m; // Zähler für Arrays
// Definieren von Variablen
function template($open='{', $close='}', $fileType='.tpl', $tplF='./') {
$this->open = $open;
$this->close = $close;
$this->values = array();
$this->fileType = $fileType;
$this->tplF = $tplF;
$this->m = 0;
}
// Den Platzhaltern Werte zuweisen
// Wenn isFile = true, wird versucht eine Datei als Wert einzubinden
function assign($var, $value, $isFile=false, $tplFolder=false) {
if($tplFolder == false) $tplFolder = $this->tplF;
if($isFile == true) {
if(!is_readable($tplFolder.$value.$this->fileType)) {
trigger_error('File <b>'.$tplFolder.$value.$this->fileType.'</b> is not readable.',ERROR);
} else {
$this->values[$var] = $this->parse(file_get_contents($tplFolder.$value.$this->fileType),2);
}
} else {
$this->values[$var] = $value;
}
}
// Ausgabe der Templates
function output($tpl) {
if(!is_readable($this->tplF.$tpl.$this->fileType)) {
trigger_error('File <b>'.$this->tplF.$tpl.$this->fileType.'</b> is not readable.',FATAL);
} else {
$content = file_get_contents($this->tplF.$tpl.$this->fileType);
echo $this->parse($content);
}
}
// replace var
function replace_var($key) {
return $this->values[$key[1]];
}
// Array ersetzen
function replace_array($ar) {
$pattern = '/'.$this->open.'array name=(.*?)'.$this->close.'(.*)'.$this->open.'\/array'.$this->close.'/is';
$return = '';
for($i=0;$i<count($this->values[$ar[1]]);$i++) {
$string = $ar[2];
if(preg_match($pattern, $string))
$string = $this->parse($string, $only=1);
foreach($this->values[$ar[1]][$i] AS $key => $val) {
$string = str_replace($this->open.$key.$this->close,$val,$string);
}
$return .= $string;
}
return $return;
}
// Parsen
function parse($string, $only=3) {
if($only != 1) {
foreach($this->values AS $key => $val) {
if(!is_array($val)) {
$string = str_replace($this->clearopen.$key.$this->clearclose,$val,$string);
}
}
}
if($only != 2) {
$pattern = '/'.$this->open.'array name=(.*)'.$this->close.'(((?R)|(.*))*)'.$this->open.'\/array'.$this->close.'/isU';
$string = preg_replace_callback($pattern,array(&$this, 'replace_array'),$string);
}
return $string;
}
} ?>
Was kann die Klasse?
Normale Platzhalter entweder durch strings oder durch Dateien ersetzen, in denen wiederum Platzhalter entahlten sein dürfen. (AFAIK unendlich tiefe verschachtelung möglich)
Ersetzen von arrays, nach dem Schema {array name=ARRAY_NAME} text {ARRAY_ITEM} {/array}. Verschachtelungen beliebig tief möglich.
MfG Oli
evtl. nutzt du
<!-- array block -->
statt
{array name=block}
dadurch lassen sich die templates besser im browser ansegen.
"ansegen"?
Ich kann ja einbauen, dass man das festlegen kann...
na, ansehen.
weil mach mal nen block bei deinem um ne tabelle. ergebnis (ungeparst) ist wahrscheinlich etwas unschön.
Sodele, da bin ich wieder.
Meine Klasse funktioniert jetzt so ziemlich einwandfrei. Man kann inzwischen selbst festlegen wie start- und end-tag eines Blockes aussehen, standard ist <!-- array name --> xyz <!-- /array -->
Außerdem werden Platzhalter, denen mehrmals ein Wert zugewiesen wird, aneinandergekettet. (also
$tpl->assign('main','hallo');
$tpl->assign('main',' du');
ergibt "hallo du")
Jetzt bin ich aber dabei ein script zu entwerfen, was diese Klasse verwenden soll - und schon bemerke ich die ersten Mängel.
Ich möchte in den script gerne alle Tage einer WOche mit einer Schleife aufzählen, soweit kein Problem, das mit der Block-Funktion der Klasse zu realisieren.
Dummerweise möchte ich bestimmten Tagen bestimmte Besonderheiten zuweisen, zum Beispiel sollte der Mittwoch anklickbar sein.
Das kann ich mit meiner Klasse jetzt aber nicht bewerkstelligen, denn die kann ja nur eine Schleife durchlaufen und die jeweiligen Array Elemente ausgeben. Ich könnte natürlich in das array jedesmal <a href="xnxn"> und </a> einspeichern, das ist aber nicht so das wahre, außerdem kann es auch mal mehr als nur ein Link sein, zum Beispiel zusätzlicher Text.
Am liebsten wäre mir, wenn das ganze wie früher lösbar wäre, (eval()). Da konnte man im Script eine Schleife machen, am Ende der Schleife hat man $var einfach den Inhalt eines Templates zugewiesen und konnte vorher so viel man wollte mit Variablen rumspielen. Jetzt hab ich aber meine Klasse und die soll das auch können, allerdings ohne zusätzliche Dateien zu benötigen, also mit Blöcken.
Um es nochmal kurz zu fassen: Ich möchte die Möglichkeit haben, Blöcke zu erstellen, deren Inhalt und Variablen die sich innerhalb des Blocks befinden, in einer Schleife im Script Werte bearbeitet werden können.
Ein Beispiel:
<!-- array tage -->
Am {wochentag} gehe ich mit {freund} spazieren.
<!-- /array -->
{wochentag} und {freund} sollen jetzt nicht Elemente des arrays "tage" sein, sondern normale Variablen, die aber bei jeder Wiederholung des Blockes andere Werte haben.
Wäre nett, wenn ihr mir erklären würdet, wie das ganze in anderen Klassen gelöst wurde bzw. ob es überhaupt sinnvoll ist, eine solche Funktion zu haben.
MfG Oli
<!-- array name --> xyz <!-- /array --> enschuldige die zwischenfrage:
wie sieht denn die regex aus, die bei deiner klasse auch verschachtelte blöcke zulässt?
Kein Problem.
So sieht die aus:
°\<\!-- array (.*) --\>(((?R)|(.*))*)\<\!-- /array --\>°isU
Hallo Leute,
Ich hänge immer noch an meiner Template Klasse (siehe Brainstorming Forum), habe aber eine Frage die allgemein besser hier reinpasst. : )
Und zwar, wie krieg ich es hin, dass ein rekursiver Aufruf eines Patterns, oder wie auch immer ich das nennen soll, zuerst den innersten Treffer ersetzt, und sich dann nach außen vorarbeitet?
Wäre toll, wenn mir jemand helfen könnte, weil das viele Probleme lösen würde.
(Alternativ wäre auch eine Lösung ok, die es mir erlaubt, verschachtelte Blöcke in Templates von Innen nach außen zu ersetzen, aber besser wäre obiges)
MfG Oli
penizillin 17-11-2004, 15:39 habe ne überlange leitung heute - könntest du vielleicht eine ungefähre struktur als (pseudo)code zeigen? mir würd's leichter fallen, darüber zu denken.
sodele,
erstmal danke für dein Interesse.
Wie rekursive Pattern in etwa funktionieren macht derHund in diesem diesem Thread (http://www.php-resource.de/forum/showthread.php?threadid=45908[/url) vor.
Dummerweise ersetzt dieser Ausdruck von außen nach Innen, sprich so:
kajshdkjha
sasdalsdhlahsdjhasd
asdajshdkhas
öhh
ALs erstes ersetzt er jetzt ales zwischen dem ersten und dem letzten und danach sucht er erst nach dem inneren.
Ich brauch es aber andersherum, er soll sich von Innen nach Außen vorarbeiten.
Warum ich das brauche ist etwas komplizierter, und im Grunde nicht relevant.
Mir würde natürlich auch eine Funktion reichen, die das irgendwie macht, aber ich glaube nicht dass sich da eine zwischen schalten kann.
MfG Oli
wie sieht denn dein pattern aus? haste n "?" hinter dem entsprechenden Quantifier?
°\<\!-- array (.*) --\>(((?R)|(.*))*)\<\!-- /array --\>°isU
so sieht der aus und funktioniert auch ziemlich gut.
Ehrlich gesagt kenne ich mich mit REGEXP nicht so gut aus, der Ausdruck da oben ist in meienm Thread im Brainstorming Forum entstanden.
Deine Quantifier Frage habe ich nicht so wirklich verstanden. Aber da oben ist ja der Pattern.
Trifft übrigens auf
<!-- array xxx --> askjdhk <!-- /array --> zu
schon mal das: http://www.php-resource.de/forum/showthread.php?s=&threadid=45908 von derHund gelesen?
btw: warum bleibst du nicht im ursprünglichen Thread? ich führe mal zusammen.
Original geschrieben von asp2php
schon mal das: http://www.php-resource.de/forum/showthread.php?s=&threadid=45908 von derHund gelesen?
btw: warum bleibst du nicht im ursprünglichen Thread? ich führe mal zusammen.
Also derHunds Thread in meiner ersten Antwort sogar verlinkt. Außerdem habe ich ja kein Problem mit dem rekursiven Pattern an sich, ich will ja nur dessen Verhalten ein wenig verändern.
Und in den anderen Thread habe ich nicht gepostet, weil das Problem 1. allgemeint verschachtelungen betrifft und ich 2. glaube ich im alten Thread auf Seite 6 nicht mehr so schnell antworten bekommen hätte. ;)
Aber danke fürs zusammenführen.
Original geschrieben von OliOli
Also derHunds Thread in meiner ersten Antwort sogar verlinkt. weißt du, wenn ich RegEx sehe, dann bekomme ich Kopfschmerzen, daher lasse ich meist die Fragen danach links liegen, daher ... ;)
ich 2. glaube ich im alten Thread auf Seite 6 nicht mehr so schnell antworten bekommen hätte. ;)
Nee, wenn das Thema einem interessiert, dann beantwortet er auch, keine Sorge. Außerdem wenn du nachher zum Ziel kommst, und irgendjemand sowas sucht dann muß er nicht enttäuscht sein, weil der Thread mitten drin aufhört :)
ja, kein Problem. Dummerweise bin ich so ungeduldig, zumal ich an dieser blöden Klasse schon lange genug sitze.
Ich hab schon auto-reload angestellt, für den thread.
zuerst den innersten Treffer ersetzt, und sich dann nach außen vorarbeitet?
hmm, wie ist es denn jetzt? (ja, umgekehrt, wirst du sicher sagen ^^).
eigentlich suchst du dir doch den match, holst dir den inneren teil, und gehst rekursiv bis zum innersten, oder nicht? dann ersetzt du doch das innerste zuerst? oder nicht?
Hm, stimmt.
Naja... Ich dummkopf..
Ich hab, zu hoffentlich guter Letzt, noch ein weiteres Problem.
Der Anwender der KLasse soll ja nicht nur die Arrays per Funktion an die Klasse weitergeben können, sondern ihnen in z.B. einer Schleife Werte zuweisen können:
<?php
for($i=0;$i<5;$i++) {
$tpl->assignB('arrayKey','Value','arrayName');
$tpl->saveB('arrayName');
}
?>
Hier weise ich dem array arrayName den Wert Value zu dem key arrayKey zu. die Funktion saveB ist dafür da, dass in der Klasse der Index des Arrays um einen erhöht wird (damit arrayKey bei jedem Schleifendurchlauf nicht überschrieben wird.)
Soweit natürlich kein Problem.
Problematisch wird es, wenn es eine weitere Schleife gibt.
Beispiel: for($i=0;$i<5;$i++) {
$tpl->assignB('arrayKey','Value','arrayName');
for($i=0;$i<5;$i++) {
$tpl->assignB('bla','wert','test');
$tpl->saveB('test');
}
$tpl->saveB('arrayName');
}
Das array test, welches in der Inneren Schleife gefüllt wird, hat jetzt natürlich 5*5 Elemente. Theoretisch ist das auch richtig so, aber da meine Parse-funktionen erst nach beiden Schleifen ausgeführt werden, werden nachher bei jedem Aufruf des Blockes test innerhalb des Blockes arrayName alle 25 Elemente angezeigt. Also sind es nachher 125 Ausgaben.
Wie kann ich das so verhindern, dass ich
a) nachher möglichst wenig zusätzliche Funktionen benötige (saveB() stört mich schon) und
b) diese Schleifen beliebig tief verschachteln kann und alles noch einwandfrei funktioniert.
MfG Oli
PS: Danke für die Hilfe übrigens, alle zusammen.
Original geschrieben von asp2php
Nee, wenn das Thema einem interessiert, dann beantwortet er auch, keine Sorge. Außerdem wenn du nachher zum Ziel kommst, und irgendjemand sowas sucht dann muß er nicht enttäuscht sein, weil der Thread mitten drin aufhört :)
Scheinbar interessiert es keinen... :(
RegEx-fanatischer Coder gibts ja auch nicht viel hier, den meisten (wie ich) wird's schon schwindlig, wenn man das Wort sieht oder hört :D
mrhappiness 18-11-2004, 16:05 Ich wollte es bei mir zuerst auch so machen, aber wenn du das umgekehrt rekursiv aufbauen willst, dann fliegst du bei so einem code auf die schnauze{loop first_array into first}
{first.field_1}<br />
{loop first.field_2 into second}
{second.field_x}
{endloop}<hr />
{first.field_3}
{endloop}wenn die innere schleife also über werte der äußeren schleife iteriert wird's haarig...
ich würde mir daher momentan einfach alle tags raussuchen, im obigen beispiel wären das 7 stück
die tags gehe ich einzeln durch, generiere php-code und solange das erste loop noch nicht geschlossen ist (hilfsarray in dem die tags der reihe draufgepackt/wieder runtergenommen werden), wird jede variable namens first entsprechend ersetzt
ich denke, damit kann ich schachteln soviel wie ich will, die umsetzung aber ist noch nicht zu 100% fertig... :)
mal davon abgesehen dass ich deinen Beitrag nur ca. zur Hälfte verstanden habe:
Was passiert denn bei deinem "into first"?
Bei mir soll die innere Verschachtelung des arrays allerdings auch ein vollkommen unabhängiges Array sein können.
MfG Oli
mrhappiness 18-11-2004, 17:28 {loop first_array into first}
{first.field_1}<br />
{loop first.field_2 into second}
{second.field_x}
{endloop}<hr />
{first.field_3}
{endloop}und{section name=first loop=$first_array}
{$first_array[first].field_1}<br />
{section name=second loop=$first_array[first].field_2}
{first_array[first].field_2[second].field_x}
{/section}
{$first_array[first].field_3}<hr />
{/section}produzieren gleiche ergebnisse, jetzt klar?
im prinzip gibt's ja bei der verschachtelung drei (kombinierbare) möglichkeiten die innere schleife iteriert über ein array, dass sich aus der äußeren schleife ergiebt (siehe beispiel) in der inneren schleife werden sowohl werte aus der inneren als auch aus der äußeren schleife ausgegeben in der inneren schleife werden keine werte aus der äußerenschleife ausgegeben oder sonst irgendwie verwendetwenn du jetzt deinen code von innen nach außen abarbeitest, fängst du ja mit der inneren schleife an.
das heißt für mich, dass du bei den möglichkeiten 1) und 2) probleme bekommen wirst, da du ja da mit werten arbeiten müsstest, die du noch gar nicht kennst
Ich mach sowas mit der PEAR Klasse Template IT
Die find ich persönlich perfekt.
Warum das Rad neu erfinden??
Original geschrieben von Foggy
Ich mach sowas mit der PEAR Klasse Template IT
Die find ich persönlich perfekt.
Warum das Rad neu erfinden??
Naja, du machst fast ausschließlich Dinge, besonders im Hobby, die irgendjemand schonmal gemacht hat.
Ich möchte das Rad gerne neu erfinden, weil ich danach wenigstens etwas stolz auf meine Klasse sein kann.
@mrhappiness:
Deine Lösung hab ich jetzt verstanden. Ich schau mal was ich draus mache.
|
-
- |