2010-07-24 4 views
5

ÜbersichtPHP - Templating mit benutzerdefinierten Tags - ist dies eine legitime Verwendung von Eval?

Gegen Ende des Jahres 2009 schrieb ich ein einfaches Template-System für PHP/HTML von unseren Designern für Broschüre-ware Typen WWW-Seiten im Hause verwendet werden. Das Ziel des Systems ist es, Templating in sonst reinem HTML über benutzerdefinierte Tags, die von PHP verarbeitet werden, zu ermöglichen. Zum Beispiel könnte eine Templat-Seite wie folgt aussehen:

<tt:Page template="templates/main.html"> 
    <tt:Content name="leftColumn"> 
    <p> blah blah </p> 
    ... 
    </tt:Content> 
    <tt:Content name="rightColumn"> 
    <p> blah blah </p> 
    ... 
    </tt:Content> 
</tt:Page> 

Die Vorlage selbst etwas könnte wie folgt aussehen:

<html> 
    <head>...</head> 
    <body> 
    <div style="float:left; width:45%"> 
     <tt:Container name="leftColumn" /> 
    </div> 
    <div style="width:45%"> 
     <tt:Container name="rightColumn" /> 
    </div> 
    </body> 
</html> 

Neben der Seite und Inhalt/Container-Tags, gibt es ein paar andere Tags enthalten im Kern für Dinge wie Flusskontrolle, Iteration über eine Sammlung, Ausgabe dynamischer Werte usw. Das Framework ist so konzipiert, dass es sehr einfach ist, eigene Tags hinzuzufügen, die unter einem anderen Präfix und Namespace registriert sind.

Benutzerdefinierte Tags PHP

Wie analysieren wir diese benutzerdefinierten Tags? Da es keine Garantie dafür gibt, dass die HTML-Datei wohlgeformtes XML ist, sind Lösungen wie XSLT/XPATH nicht zuverlässig. Stattdessen verwenden wir eine Regex, um nach Tags mit registrierten Präfixen zu suchen und diese durch PHP-Code zu ersetzen. Der PHP-Code ist ein Stack-basiertes Design ... wenn ein öffnendes Tag angetroffen wird, wird ein Objekt, das das Tag repräsentiert, auf den Stapel geschoben und seine "Initialisierungsfunktion" (falls vorhanden) wird ausgeführt. Immer wenn ein registriertes schließendes Tag angetroffen wird, wird das letzte Objekt vom Stapel entfernt und seine "Rendering-Funktion" wird ausgeführt.

So, nachdem der Rahmen für die Templat-Tags mit PHP ersetzt, könnte unser Beispiel-Seite wie folgt aussehen (in Objekt es ist ein bisschen hässlicher):

<?php $tags->push('tt', 'Page', array('template'=>'templates/main.html')); ?> 
    <?php $tags->push('tt', 'Content', array('name'=>'leftColumn')); ?> 
    <p> blah blah </p> 
    ... 
    <?php $tags->pop(); ?> 
    <?php $tags->push('tt', 'Content', array('name'=>'rightColumn')); ?> 
    <p> blah blah </p> 
    ... 
    <?php $tags->pop(); ?> 
<?php $tags->pop(); ?> 

Das Gute, das Schlechte und eval

Nun, wie wird unser neu generierter PHP Code ausgeführt? Ich denke hier an ein paar Optionen. Am einfachsten ist es einfach, eval die Zeichenfolge, und das funktioniert gut genug. Allerdings wird jeder Programmierer Ihnen sagen: "Eval ist böse, benutze es nicht ...", also lautet die Frage: Gibt es etwas passenderes als eval, das wir hier verwenden können?

Ich habe überlegt, eine temporäre oder zwischengespeicherte Datei zu verwenden, php:// Ausgabeströme, etc., aber soweit ich sehe, bieten diese keinen wirklichen Vorteil gegenüber eval. Caching könnte die Dinge beschleunigen, aber in der Praxis sind alle Seiten, die wir zu diesem Thema haben, bereits blitzschnell, daher sehe ich an diesem Punkt keine Notwendigkeit, Geschwindigkeitsoptimierungen vorzunehmen.

Fragen

Für jede der Dinge auf dieser Liste: ist es eine gute Idee? Kannst du dir eine bessere Alternative vorstellen?

  • die ganze Idee im Allgemeinen (Custom Tags für html/php)
  • Tags PHP-Code statt Verarbeitung direkt
  • der Stack-basierter Ansatz
  • die Verwendung von eval (oder ähnlich) Umwandlung

Vielen Dank für das Lesen und TIA für einen Ratschlag. :)

+0

+1 für eine gut formatierte, gut geschriebene und gut durchdachte Frage. – gpmcadam

+0

Danke. OT: Ich mag deinen Avatar. Gigantor von Evol Intent trug ein T-Shirt mit dem gleichen Design, als er vor ein paar Jahren hier durchkam. Ist es ein Logo für ein Plattenlabel oder etwas oder nur ein zufälliges Design? Ich habe mich das schon seit Jahren gefragt. –

Antwort

2

Lassen Sie mich einen anderen Ansatz befürworten. Statt PHP-Code dynamisch zu generieren und dann herauszufinden, wie er sicher ausgeführt werden kann, führen Sie ihn direkt aus, wenn Sie auf die Tags stoßen. Sie können den gesamten HTML-Block in einem Durchgang verarbeiten und jedes Tag so handhaben, wie Sie es sofort finden.

Schreiben Sie eine Schleife, die nach Tags sucht. Seine Grundstruktur wird wie folgt aussehen:

  1. Suchen Sie nach einer benutzerdefinierten Tags, die Sie an der Position n finden.
  2. Alles vor Position n muss einfach HTML sein, also speichern Sie es entweder für die Verarbeitung oder geben Sie es sofort aus (wenn Sie keine Tags auf Ihrem $tags Stapel haben, müssen Sie es wahrscheinlich nirgendwo speichern).
  3. Führen Sie den entsprechenden Code für das Tag aus. Anstatt Code zu generieren, der $tags->push aufruft, rufen Sie einfach $tags->push direkt an.
  4. Zurück 1.

Mit diesem Ansatz rufen Sie nur Funktionen PHP Sie nie direkt on the fly PHP-Code bauen, zu treten und es dann später auszuführen. Die Notwendigkeit für eval ist weg.

Sie haben grundsätzlich zwei Fälle für Schritt # 3. Wenn Sie auf ein öffnendes Tag stoßen, machen Sie sofort eine push. Wenn Sie später auf das schließende Tag klicken, können Sie eine pop ausführen und dann das Tag in der entsprechenden Weise behandeln, nachdem Sie den gesamten Inhalt des benutzerdefinierten Elements verarbeitet haben.

Es ist auch effizienter, den HTML-Code auf diese Weise zu verarbeiten. Das Ausführen mehrerer Such- und Ersetzungsvorgänge in einer langen HTML-Zeichenfolge ist ineffizient, da jede Suche und jede Ersetzung 0 ( n) für die Länge der Zeichenfolge ist.Das bedeutet, dass Sie die Zeichenfolge wiederholt wiederholt durchsuchen und jedes Mal, wenn Sie einen Ersatz vornehmen, müssen Sie ganz neue Zeichenfolgen ähnlicher Länge generieren. Wenn Sie 20KB HTML haben, beinhaltet jeder Ersatz das Durchsuchen dieser 20KB und das anschließende Erstellen einer neuen 20KB-Zeichenfolge.

+0

John, das klingt nach dem richtigen Weg, um es für streng reines HTML (kein PHP) zu tun, und ich werde eine Chance darauf nehmen. Aber als ich das Template-System entworfen habe, dachte ich, es wäre nett, wenn du PHP einbinden könntest, und ich denke, einige der Seiten haben es jetzt gemischt. Vielleicht war das eine schlechte Design-Idee, aber ich denke, dass es nun den Einsatz von 'eval' oder ähnlich. Auch zur Entwurfszeit dachte ich, das generierte PHP Caching wäre eine gute Idee. Denkst du, dass das hier mit deinem Ansatz vergleichbar oder schneller ist? –

+0

Caching würde die Leistungsbedenken mindern, so dass Sie sich nur um den Sicherheitsaspekt kümmern müssen. –

+0

Wenn Sie Benutzern erlauben, in PHP-Code zu mischen, dann ist das ein ganzer 'nother Ball aus Wachs. Sobald Sie willkürlichen Code ausführen, ist die Sorge um einen Aufruf von eval ziemlich sinnlos. Ihre Sicherheitsbedenken würden sich dann ändern, um den PHP-Code zu sandboxieren, PHP safe_mode zu verwenden, Benutzerberechtigungen zu sperren, so dass sie keine zufälligen system() -Aufrufe machen können. –

0

Ja, statt eval, können Sie tun, was zend und andere wichtige Frameworks, Ausgabepufferung verwenden:

ob_start(); 
include($template_file); //has some HTML and output generating PHP 
$result = ob_get_contents(); 
ob_end_clean(); 
+0

Dies würde bedeuten, den PHP-Zwischencode, der beim Parsing des Markups generiert wird, in eine Datei zu speichern und dann einzuschließen. Dies beinhaltet eine Menge von Festplattenzugriff, die nicht einfach mit "eval" funktioniert. Was ist der Vorteil, es so zu machen? –

+0

Ich sehe was du meinst. Diese Antwort ist nicht falsch, aber nicht realistisch. Ich werde eine andere, vernünftigere Antwort posten –

+0

@no, meine neue Antwort wurde gepostet. –

2

ich eine andere Antwort geschrieben, weil es von den ersten radikal anders ist, was auch sein könnte wertvoll.

Im Wesentlichen fragt diese Frage, wie PHP-Code mit Regex ausgeführt wird. Es mag nicht so offensichtlich erscheinen, aber das ist es, was die Evaluatorin erreichen will.

Mit diesem gesagt, anstatt einen Pass von preg_replace zu tun und dann ein Eval, könnten Sie einfach PHP preg_replace_callback Funktion, um einen Teil des Codes ausgeführt, wenn abgestimmt.

finden Sie hier, wie die Funktion arbeitet: http://us.php.net/manual/en/function.preg-replace-callback.php

+0

Mike, sehen Sie meine Antwort an John (Sie sagen so ziemlich das gleiche, was ich denke). –

Verwandte Themen