PHP – Vorlagen mit benutzerdefinierten Tags – ist dies eine legitime Verwendung von eval?

StackOverflow https://stackoverflow.com/questions/3326446

  •  29-09-2020
  •  | 
  •  

Frage

Überblick

Etwa Ende 2009 habe ich ein einfaches Vorlagensystem für PHP/HTML geschrieben, das von unseren Designern intern für Websites im Broschürenformat verwendet werden sollte.Das Ziel des Systems besteht darin, Vorlagen in ansonsten reinem HTML über benutzerdefinierte Tags zu ermöglichen, die von PHP verarbeitet werden.Eine mit Vorlagen versehene Seite könnte beispielsweise so 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 könnte etwa so 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 den Tags „Seite“ und „Inhalt/Container“ sind im Kern noch einige andere Tags für Dinge wie Flusskontrolle, Iteration über eine Sammlung, Ausgabe dynamischer Werte usw. enthalten.Das Framework ist so konzipiert, dass es sehr einfach ist, eigene Tags hinzuzufügen, die unter einem anderen Präfix und Namensraum registriert sind.

Benutzerdefinierte Tags für PHP

Wie analysieren wir diese benutzerdefinierten Tags?Da es keine Garantie dafür gibt, dass es sich bei der HTML-Datei um wohlgeformtes XML handelt, sind Lösungen wie XSLT/XPATH nicht zuverlässig.Stattdessen verwenden wir einen regulären Ausdruck, um nach Tags mit registrierten Präfixen zu suchen und diese durch PHP-Code zu ersetzen.Der PHP-Code ist ein Stack-basiertes Design...Beim Auftreffen auf ein öffnendes Tag wird ein Objekt erstellt, das das Tag darstellt, auf den Stapel verschoben und seine „Initialisierungsfunktion“ (falls vorhanden) ausgeführt.Immer wenn ein registriertes schließendes Tag angetroffen wird, wird das aktuellste Objekt vom Stapel entfernt und seine „Rendering-Funktion“ wird ausgeführt.

Nachdem das Framework also die Template-Tags durch PHP ersetzt hat, könnte unsere Beispielseite etwa so aussehen (in Wirklichkeit ist sie etwas 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

Wie führen wir nun unseren neu generierten PHP-Code aus?Mir fallen hier einige Möglichkeiten ein.Am einfachsten ist es einfach eval die Saite, und das funktioniert gut genug.Allerdings wird Ihnen jeder Programmierer sagen: „Eval ist böse, verwenden Sie es nicht …“ Die Frage ist also, ob es etwas Passenderes gibt als eval dass wir hier verwenden können?

Ich habe darüber nachgedacht, eine temporäre oder zwischengespeicherte Datei zu verwenden php:// Ausgabeströme usw., aber soweit ich sehen kann, bieten diese keinen wirklichen Vorteil gegenüber eval.Caching könnte die Dinge beschleunigen, aber in der Praxis sind alle Websites, die wir auf diesem Ding haben, bereits rasend schnell, sodass ich an dieser Stelle keinen Bedarf für Geschwindigkeitsoptimierungen sehe.

Fragen

Für jedes der Dinge auf dieser Liste:Ist es eine gute Idee?Können Sie sich eine bessere Alternative vorstellen?

  • die ganze Idee im Allgemeinen (benutzerdefinierte Tags für HTML / PHP)
  • Konvertieren von Tags in PHP-Code, anstatt sie direkt zu verarbeiten
  • der stapelbasierte Ansatz
  • die Verwendung von eval (o.ä)

Vielen Dank fürs Lesen und TIA für jeden Rat.:) :)

War es hilfreich?

Lösung

Lassen Sie mich einen anderen Ansatz vertreten.Anstatt 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.Du kannst Verarbeiten Sie den gesamten HTML-Block in einem Durchgang und behandeln Sie jedes Tag, wenn Sie darauf stoßen sofort.

Schreiben Sie eine Schleife, die nach Tags sucht.Seine Grundstruktur sieht folgendermaßen aus:

  1. Suchen Sie nach einem benutzerdefinierten Tag, das Sie unter Position finden N.
  2. Alles vor Position N Es muss sich um einfaches HTML handeln. Speichern Sie es daher entweder zur Verarbeitung oder geben Sie es sofort aus (wenn Sie keine Tags auf Ihrem HTML-Code haben). $tags Stapel müssen Sie ihn wahrscheinlich nirgendwo speichern).
  3. Führen Sie den entsprechenden Code für das Tag aus.Anstatt Code zu generieren, der aufruft $tags->push, Ruf einfach an $tags->push direkt.
  4. Gehen Sie zurück zu Schritt 1.

Bei diesem Ansatz rufen Sie PHP-Funktionen nur direkt auf, erstellen PHP-Code nie spontan und führen ihn dann später aus.Das Bedürfnis nach eval ist weg.

Für Schritt 3 stehen Ihnen grundsätzlich zwei Fälle zur Verfügung.Wenn Sie auf ein öffnendes Tag stoßen, führen Sie sofort eine Aktion durch push.Wenn Sie dann später auf das schließende Tag klicken, können Sie Folgendes tun: pop und behandeln Sie das Tag dann auf die entsprechende Weise, nachdem Sie nun den gesamten Inhalt des benutzerdefinierten Elements verarbeitet haben.

Außerdem ist es effizienter, den HTML-Code auf diese Weise zu verarbeiten.Das mehrfache Suchen und Ersetzen einer langen HTML-Zeichenfolge ist ineffizient, da jede Suche und jede Ersetzung O(N) auf der Länge der Zeichenfolge.Das heißt, Sie scannen die Zeichenfolge immer wieder und jedes Mal, wenn Sie eine Ersetzung vornehmen, müssen Sie ganz neue Zeichenfolgen ähnlicher Länge generieren.Wenn Sie über 20 KB HTML verfügen, müssen bei jedem Ersetzen diese 20 KB durchsucht und anschließend eine neue 20 KB große Zeichenfolge erstellt werden.

Andere Tipps

Ich habe eine weitere Antwort gepostet, weil sie sich grundlegend von der ersten unterscheidet, was ebenfalls wertvoll sein könnte.

Im Wesentlichen geht es bei dieser Frage darum, wie man PHP-Code mit Regex ausführt.Es mag nicht so offensichtlich erscheinen, aber genau das soll mit der Bewertung erreicht werden.

Anstatt jedoch preg_replace zu durchlaufen und dann eine Auswertung durchzuführen, könnten Sie einfach die preg_replace_callback-Funktion von PHP verwenden, um bei Übereinstimmung einen Codeabschnitt auszuführen.

Hier erfahren Sie, wie die Funktion funktioniert: http://us.php.net/manual/en/function.preg-replace-callback.php

Ja, anstelle von eval können Sie das tun, was Zend und andere wichtige Frameworks tun: Ausgabepufferung verwenden:

ob_start();
include($template_file); //has some HTML and output generating PHP
$result = ob_get_contents();
ob_end_clean();
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top