PHP - modelli con tag personalizzati - è un uso legittimo di eval?
-
29-09-2020 - |
Domanda
Panoramica
Verso la fine del 2009, ho scritto un semplice sistema di template per PHP/HTML da utilizzare internamente dai nostri designer per siti web di tipo brochure.L'obiettivo del sistema è consentire la creazione di modelli in HTML altrimenti puro tramite tag personalizzati elaborati da PHP.Ad esempio, una pagina basata su modello potrebbe assomigliare a questa:
<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>
Il modello stesso potrebbe assomigliare a questo:
<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>
Oltre ai tag Pagina e Contenuto/Contenitore, ci sono alcuni altri tag inclusi nel core per cose come il controllo del flusso, l'iterazione su una raccolta, l'output di valori dinamici, ecc.Il framework è progettato in modo che sia molto semplice aggiungere il proprio set di tag registrati con un altro prefisso e spazio dei nomi.
Tag personalizzati in PHP
Come analizziamo questi tag personalizzati?Poiché non esiste alcuna garanzia che il file HTML sia XML ben formato, soluzioni come XSLT/XPATH non saranno affidabili.Utilizziamo invece una regex per cercare tag con prefissi registrati e sostituirli con codice PHP.Il codice PHP è un design basato su stack...quando si incontra un tag di apertura, un oggetto che rappresenta il tag viene creato inserito nello stack e la sua "funzione di inizializzazione" (se presente) viene eseguita.Ogni volta che viene incontrato un tag di chiusura registrato, l'oggetto più recente viene estratto dallo stack e viene eseguita la sua "funzione di rendering".
Quindi, dopo che il framework ha sostituito i tag di template con PHP, la nostra pagina di esempio potrebbe assomigliare a questa (in realtà è un po' più brutta):
<?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(); ?>
Il buono, il cattivo e eval
Ora, come eseguire il nostro codice PHP appena generato?Mi vengono in mente alcune opzioni qui.Il più semplice è semplicemente eval
la stringa, e funziona abbastanza bene.Tuttavia, qualsiasi programmatore ti dirà "eval è malvagio, non usarlo..." quindi la domanda è: c'è qualcosa di più appropriato di eval
che possiamo usare qui?
Ho preso in considerazione l'utilizzo di un file temporaneo o memorizzato nella cache, utilizzando php://
flussi di output, ecc., ma per quanto posso vedere questi non offrono alcun vantaggio reale rispetto a eval
.La memorizzazione nella cache potrebbe velocizzare le cose, ma in pratica tutti i siti che abbiamo su questa cosa sono già incredibilmente veloci, quindi non vedo la necessità di ottimizzare la velocità a questo punto.
Domande
Per ciascuna delle cose in questo elenco:è una buona idea?Riesci a pensare a un'alternativa migliore?
- l'intera idea in generale (tag personalizzati per html/php)
- convertire i tag in codice php invece di elaborarli direttamente
- l’approccio basato sullo stack
- l'impiego di
eval
(o simili)
Grazie per la lettura e TIA per qualsiasi consiglio.:)
Soluzione
Vorrei sostenere un approccio diverso.Invece di generare codice PHP in modo dinamico e poi cercare di capire come eseguirlo in modo sicuro, eseguilo direttamente quando incontri i tag.Puoi elabora l'intero blocco di HTML in un unico passaggio e gestisci ogni tag man mano che lo incontri subito.
Scrivi un ciclo che cerchi i tag.La sua struttura di base sarà simile a questa:
- Cerca un tag personalizzato, che trovi in posizione N.
- Tutto prima della posizione N deve essere un semplice HTML, quindi salvalo per l'elaborazione o visualizzalo immediatamente (se non hai tag sul tuo file
$tags
stack probabilmente non è necessario salvarlo da nessuna parte). - Esegui il codice appropriato per il tag.Invece di generare codice che chiama
$tags->push
, chiama soltanto$tags->push
direttamente. - Torna al passaggio 1.
Con questo approccio chiami direttamente solo le funzioni PHP, non costruisci mai il codice PHP al volo per poi eseguirlo in seguito.Il bisogno di eval
è andato.
Avrai fondamentalmente due casi per il passaggio n. 3.Quando incontri un tag di apertura, eseguirai un'operazione immediata push
.Successivamente, quando premi il tag di chiusura, puoi fare a pop
e poi gestisci il tag nel modo appropriato, ora che hai elaborato l'intero contenuto dell'elemento personalizzato.
È anche più efficiente elaborare l'HTML in questo modo.Effettuare ricerche e sostituzioni multiple su una stringa HTML lunga è inefficiente poiché ogni ricerca e ogni sostituzione è O(N) sulla lunghezza della corda.Ciò significa che stai scansionando ripetutamente la stringa più e più volte e ogni volta che esegui una sostituzione devi generare stringhe completamente nuove di lunghezza simile.Se disponi di 20 KB di HTML, ogni sostituzione implica la ricerca in quei 20 KB e la creazione successiva di una nuova stringa da 20 KB.
Altri suggerimenti
Ho postato un'altra risposta perché è radicalmente diverso dal primo, che potrebbe anche essere prezioso.
In sostanza, questa domanda sta chiedendo come eseguire il codice PHP con Regex.Potrebbe non sembrare così ovvio, ma questo è ciò che la Valta ha intenzione di realizzare.
Con ciò detto, invece di fare un passaggio di preg_replace e poi fare una valutazione, è possibile utilizzare la funzione PREG_REPLACE_CALLBACK di PHP per eseguire un pezzo di codice quando è abbinato.
Vedi qui per come funziona la funzione: http://us.php.net/manual/en/function.preg-replace-callback.php
Sì, invece di Eval, puoi fare ciò che lo sviluppo di zend e altri importanti framework, utilizzare il buffering di uscita:
ob_start();
include($template_file); //has some HTML and output generating PHP
$result = ob_get_contents();
ob_end_clean();
.