2010-07-24 8 views
5

PanoramicaPHP - template con tag personalizzati - è un uso legittimo di eval?

Intorno alla fine del 2009, ho scritto un semplice sistema di template per PHP/HTML da utilizzare in-house dai nostri progettisti per siti web tipo brochure-ware. L'obiettivo del sistema è consentire il template in HTML altrimenti puro tramite tag personalizzati elaborati da PHP. Ad esempio, una pagina su modelli potrebbe assomigliare a questo:

<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 essere simile a questa:

<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 alla pagina e contenuti/tag container, ci sono un paio di altri tag inclusi nel nucleo per cose come il controllo del flusso, l'iterazione su una collezione, l'output di valori dinamici, ecc. Il framework è progettato in modo che sia molto facile aggiungere il proprio insieme di tag registrati sotto un altro prefisso e spazio dei nomi.

personalizzati Tag a PHP

Come analizziamo questi tag personalizzati? Poiché non si garantisce che il file HTML sia un XML ben formato, soluzioni come XSLT/XPATH non saranno affidabili. Invece, usiamo un'espressione regolare per cercare tag con prefissi registrati e sostituirli con codice PHP. Il codice PHP è un progetto basato su stack ... dopo aver incontrato un tag di apertura, un oggetto che rappresenta il tag viene creato inserito nello stack e viene eseguita la sua "funzione di inizializzazione" (se presente). Ogni volta che viene rilevato un tag di chiusura registrato, l'oggetto più recente viene estratto dallo stack e viene eseguita la sua "funzione di rendering".

Così, dopo il quadro sostituisce i tag di template con PHP, la nostra pagina di esempio potrebbe essere simile a questo (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 brutto e eval

Ora, come eseguire il nostro codice PHP appena generato? Posso pensare ad alcune opzioni qui. Il più semplice è semplicemente la stringa eval, che 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 considerato l'utilizzo di un file temporaneo o memorizzato nella cache, utilizzando i flussi di output php://, ecc., Ma per quanto posso vedere questi non offrono alcun vantaggio reale rispetto a eval. Il caching potrebbe accelerare le cose, ma in pratica tutti i siti che abbiamo su questa cosa sono già incredibilmente veloci, quindi a questo punto non vedo la necessità di ottimizzare la velocità.

Domande

Per ciascuna delle cose su questa lista: è una buona idea? Riesci a pensare ad un'alternativa migliore?

  • l'idea in generale (tag personalizzati per HTML/PHP)
  • conversione dei tag di codice php invece di elaborazione direttamente
  • l'approccio basato su stack
  • l'uso di eval (o simile)

Grazie per la lettura e TIA per qualsiasi consiglio. :)

+0

+1 per una domanda ben formattata, ben scritta e ben pensata. – gpmcadam

+0

Grazie. OT: Mi piace il tuo avatar. Gigantor di Evol Intent indossava una maglietta con lo stesso identico design quando è venuto qui alcuni anni fa. È un logo per un'etichetta discografica o qualcosa del genere, o solo un disegno casuale? Lo stai chiedendo da anni. –

risposta

2

Vorrei difendere un approccio diverso. Invece di generare codice PHP in modo dinamico e poi cercare di capire come eseguirlo in modo sicuro, eseguirlo direttamente mentre incontri i tag. Puoi elaborare l'intero blocco di codice HTML in un'unica passata e gestire ogni tag quando lo incontri immediatamente.

Scrivere un ciclo che cerchi i tag. La sua struttura di base sarà simile a questa:

  1. Cercare un tag personalizzato, che si trova alla posizione n .
  2. Tutto prima della posizione n deve essere semplice HTML, quindi salvarlo per l'elaborazione o inviarlo immediatamente (se non ci sono tag sullo stack $tags probabilmente non è necessario salvarlo da nessuna parte).
  3. Esegui il codice appropriato per il tag. Invece di generare codice che chiama $tags->push, chiama direttamente lo $tags->push.
  4. tornare al punto 1.

Con questo approccio si chiama solo funzioni PHP direttamente, non hai mai costruire codice PHP al volo e quindi eseguire in un secondo momento. La necessità di eval non esiste più.

In pratica avrete due casi per il passaggio n. 3. Quando incontri un tag di apertura, esegui un immediato push. Successivamente, quando premi il tag di chiusura, puoi fare un pop e poi gestirlo nel modo appropriato, ora che hai elaborato l'intero contenuto dell'elemento personalizzato.

È anche più efficiente elaborare l'HTML in questo modo. Fare ricerche multiple e sostituirle su una lunga stringa HTML è inefficiente dato che ogni ricerca e ogni sostituzione sono O ( n) sulla lunghezza della stringa.Significa che stai ripetutamente scansionando la stringa più e più volte, e ogni volta che fai una sostituzione devi generare stringhe completamente nuove di lunghezza simile. Se disponi di 20 KB di HTML, ogni sostituzione comporta la ricerca in tale 20 KB e successivamente la creazione di una nuova stringa da 20 KB.

+0

John, questo suona come il modo corretto di farlo per HTML rigorosamente puro (senza PHP), e ci proverò. Tuttavia, quando ho progettato il sistema di template ho pensato che sarebbe stato bello poter inserire PHP in PHP, e penso che alcuni dei siti ora lo abbiano mixato. Forse è stata una cattiva idea di design, ma penso che ora sia necessario l'uso di 'eval' o simile. Inoltre, in fase di progettazione ho pensato che la memorizzazione nella cache del PHP generato sarebbe stata una buona idea. Pensi che potrebbe essere paragonabile o più veloce del tuo approccio qui? –

+0

Il caching attenuerebbe i problemi di prestazioni, quindi dovresti solo preoccuparti dell'aspetto della sicurezza. –

+0

Se si consente agli utenti di mixare il codice PHP, allora è tutta una cerchia di cera. Una volta che si sta eseguendo codice arbitrario, preoccuparsi di una chiamata alla valutazione è praticamente inutile. I tuoi problemi di sicurezza cambieranno in sandboxing il codice PHP, usando PHP safe_mode, bloccando le autorizzazioni degli utenti in modo che non possano effettuare chiamate casuali di sistema(), ecc. –

0

Sì, invece di eval, si può fare quello Zend e altri importanti programmi di fare, usare il buffering:

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

Ciò significherebbe salvare il codice PHP intermedio (che viene generato dall'analisi del markup) in un file e quindi includerlo. Ciò comporta un sacco di accesso al disco che semplicemente utilizzando 'eval' non lo fa. Qual è il vantaggio di farlo in questo modo? –

+0

Capisco cosa intendi. Questa risposta non è sbagliata, ma non è realistica qui allora. Pubblicherò un'altra risposta più sana di me, –

+0

, la mia nuova risposta è stata pubblicata. –

2

ho inviato un'altra risposta, perché è radicalmente diverso dal primo, che potrebbe anche essere prezioso.

In sostanza, questa domanda chiede come eseguire codice PHP con regex. Potrebbe non sembrare così ovvio, ma questo è ciò che la valutazione intende raggiungere.

Detto ciò, anziché eseguire un passaggio di preg_replace e quindi eseguire un'eval, è possibile utilizzare la funzione preg_replace_callback di PHP per eseguire una parte di codice quando associata.

vedi qui per come funziona la funzione: http://us.php.net/manual/en/function.preg-replace-callback.php

+0

Mike, vedi la mia risposta a John (voi ragazzi state dicendo praticamente la stessa cosa che penso). –

Problemi correlati