2010-07-24 18 views
5

generalPHP - creación de plantillas con etiquetas personalizadas - ¿este es un uso legítimo de eval?

A finales del 2009, escribí un sencillo sistema de plantillas para PHP/HTML para ser utilizado internamente por nuestros diseñadores de páginas web tipo folleto-ware. El objetivo del sistema es permitir la creación de plantillas en HTML, por lo demás puro, a través de etiquetas personalizadas que PHP procesa. Por ejemplo, una página de plantilla podría tener este aspecto:

<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> 

la propia plantilla podría ser algo como esto:

<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> 

Además de la página y las etiquetas de contenido/el recipiente, hay algunas otras etiquetas incluidas en el núcleo para cosas como control de flujo, iterar sobre una colección, generar valores dinámicos, etc. El marco está diseñado por lo que es muy fácil agregar su propio conjunto de etiquetas registradas bajo otro prefijo y espacio de nombres.

etiquetas personalizadas para PHP

¿Cómo podemos analizar sintácticamente estas etiquetas personalizadas? Como no hay garantía de que el archivo HTML esté bien formado en XML, las soluciones como XSLT/XPATH no serán confiables. En cambio, usamos una expresión regular para buscar las etiquetas con prefijos registrados, y las reemplazamos con código PHP. El código PHP es un diseño basado en pila ... al encontrar una etiqueta de apertura, un objeto que representa la etiqueta se crea insertado en la pila y se ejecuta su "función de inicialización" (si corresponde). Cada vez que se encuentra una etiqueta de cierre registrada, el objeto más reciente se saca de la pila y se ejecuta su "función de representación".

Así, después de que el marco sustituye a las etiquetas de plantillas con PHP, nuestra página de ejemplo podría ser algo como esto (en bienes raíces es un feo bits):

<?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(); ?> 

El bueno, el malo, y eval

Ahora, ¿cómo ejecutar nuestro código PHP recién generado? Puedo pensar en algunas opciones aquí. Lo más fácil es simplemente eval la cadena, y eso funciona bastante bien. Sin embargo, cualquier programador le dirá "eval es malo, no lo use ..." entonces la pregunta es, ¿hay algo más apropiado que eval que podamos usar aquí?

He considerado utilizar un archivo temporal o en caché, usando php:// flujos de salida, etc., pero hasta donde puedo ver, estos no ofrecen ninguna ventaja real sobre eval. El almacenamiento en caché puede acelerar las cosas, pero en la práctica todos los sitios que tenemos sobre este tema ya son increíblemente rápidos, así que no veo la necesidad de optimizar la velocidad en este punto.

Preguntas

Para cada una de las cosas en esta lista: ¿Es una buena idea? ¿Puedes pensar en una mejor alternativa?

  • toda la idea en general (etiquetas personalizadas para HTML/PHP)
  • conversión de etiquetas de código php en lugar de procesamiento directamente
  • el enfoque basado en la pila
  • el uso de eval (o similar)

Gracias por la lectura y la TIA para cualquier consejo. :)

+0

+1 para una pregunta bien formateada, bien escrita y bien pensada. – gpmcadam

+0

Gracias. OT: me gusta tu avatar. Gigantor de Evol Intent llevaba una camiseta con ese mismo diseño exactamente cuando llegó aquí hace unos años. ¿Es un logotipo para una disquera o algo así, o simplemente un diseño al azar? Me he estado preguntando eso por años. –

Respuesta

2

Let Me defiendo un enfoque diferente. En lugar de generar código PHP dinámicamente y luego tratar de averiguar cómo ejecutarlo de manera segura, ejecútelo directamente a medida que encuentre las etiquetas. Puede procesar el bloque completo de HTML en una sola pasada y manejar cada etiqueta como lo encuentra inmediatamente.

escribe un bucle que busca las etiquetas. Su estructura básica se verá así:

  1. Busque una etiqueta personalizada, que se encuentra en la posición n .
  2. Todo antes de la posición n debe ser HTML simple, entonces guárdelo para procesarlo o envíelo inmediatamente (si no tiene etiquetas en su pila $tags, probablemente no necesite guardarlo en ninguna parte).
  3. Ejecute el código apropiado para la etiqueta. En lugar de generar código que llame al $tags->push, simplemente llame al $tags->push directamente.
  4. volver al paso 1.

Con este enfoque sólo llamar directamente las funciones de PHP, nunca se construye código PHP sobre la marcha y luego ejecutarlo más tarde. La necesidad de eval ha desaparecido.

Usted tiene básicamente dos casos para el paso # 3. Cuando encuentre una etiqueta de apertura, hará un inmediato push. Luego, más adelante, cuando toques la etiqueta de cierre, puedes hacer un pop y luego manejar la etiqueta de la manera adecuada, ahora que has procesado todo el contenido del elemento personalizado.

También es más eficiente para procesar el código HTML de esta manera. Hacer múltiples búsquedas y reemplazos en una cadena larga de HTML es ineficaz ya que cada búsqueda y cada reemplazo es O ( n) en la longitud de la cadena.Lo que significa que está escaneando repetidamente la cadena una y otra vez, y cada vez que hace un reemplazo, debe generar cadenas totalmente nuevas de longitud similar. Si tiene 20 KB de HTML, cada reemplazo implica buscar a través de esos 20 KB y luego crear una nueva cadena de 20 KB después.

+0

John, esto suena como la forma correcta de hacerlo por HTML estrictamente puro (sin PHP), y voy a intentarlo. Sin embargo, cuando diseñé el sistema de plantillas pensé que sería bueno si pudieras mezclar PHP, y creo que algunos de los sitios ahora lo tienen mezclado. Tal vez esa fue una mala idea de diseño, pero creo que ahora necesita el uso de 'eval' o similar. Además, en el momento del diseño pensé que almacenar en caché el PHP generado sería una buena idea. ¿Crees que podría ser comparable o más rápido que tu enfoque aquí? –

+0

El almacenamiento en caché mitigaría las preocupaciones sobre el rendimiento, por lo que solo tendría que preocuparse por el aspecto de seguridad. –

+0

Si permites que los usuarios mezclen el código PHP, entonces es una bola de cera completa. Una vez que está ejecutando código arbitrario, preocuparse por una llamada a eval es bastante inútil. Sus preocupaciones de seguridad cambiarían entonces a la zona de pruebas del código PHP, utilizando el safe_mode de PHP, bloqueando los permisos de los usuarios para que no puedan hacer llamadas al sistema() al azar, etc. –

0

Si, en lugar de eval, puede hacer lo Zend y otros marcos principales no, utilizar el almacenamiento en búfer de salida:

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

Esto significaría guardar el código PHP intermedio (que se genera al analizar el marcado) en un archivo y luego incluirlo. Esto implica mucho acceso a disco que simplemente usar 'eval' no. ¿Cuál es la ventaja de hacerlo de esta manera? –

+0

Veo a qué te refieres. Esta respuesta no está mal, pero no es realista aquí entonces. Publicaré otra respuesta más sensata, –

+0

@no, mi nueva respuesta ha sido publicada. –

2

he publicado otra respuesta porque es radicalmente diferente de la primera, que también podría ser valioso.

En esencia, esta pregunta está pidiendo a la forma de ejecutar código PHP con expresiones regulares. Puede no parecer tan obvio, pero esto es lo que el eval está intentando lograr.

Dicho esto, en lugar de hacer un pase de preg_replace y luego hacer una evaluación, puedes usar la función preg_replace_callback de PHP para ejecutar un fragmento de código cuando coincida.

Vea aquí cómo funciona la función: http://us.php.net/manual/en/function.preg-replace-callback.php

+0

Mike, mira mi respuesta a John (ustedes están diciendo más o menos lo mismo, creo). –

Cuestiones relacionadas