2011-08-08 6 views
7

Antes de pasar una cadena a eval() Me gustaría asegurarse de que la sintaxis es correcta y permitir:Validar el usuario introduce el código PHP antes de pasarla a eval()

  1. Dos funciones: a() y b()
  2. cuatro operadores:/* - +
  3. Soportes:()
  4. Números: 1,2, -1, 1

¿Cómo puedo hacer esto, tal vez tiene algo que ver con PHP Tokenizer?

Estoy tratando de hacer un intérprete de fórmula simple por lo que a() y b() serán reemplazados por ln() y exp(). No quiero escribir un tokenizer y un analizador desde cero.

+0

¿Te importa el orden de esas posibles entradas? – hoppa

+0

Se debe agregar a su pregunta un ejemplo de la función que permitiría y una función que no debería aprobar. Tenga en cuenta que el uso de eval no se debe tomar a la ligera. –

+2

Es por eso que quiere desinfectar de antemano sospecho;) – hoppa

Respuesta

2

generadores de analizadores sintácticos lo han hecho han escrito para PHP, y "CAL" en particular viene con el típico ejemplo de "calculadora", lo que sería un punto de partida obvio para su "mini lenguaje": http://sourceforge.net/projects/lime-php/

Ha sido años desde la última vez que jugué con LIME, pero ya estaba maduro & estable entonces.

Notas:

1) Usando un completo en generador de análisis le da la ventaja de evitar eval() de PHP por completo si lo desea - puede hacer CAL emite un programa de análisis que proporciona efectivamente una función de "eval" para expresiones escritas en su mini idioma (con la validación incorporada). Esto le brinda la ventaja adicional de permitirle agregar soporte para nuevas funciones, según sea necesario.

2) Puede parecer demasiado rápido al principio usar un generador de analizadores para una tarea aparentemente tan pequeña, pero una vez que obtenga los ejemplos funcionando, quedará impresionado por lo fácil que es modificarlos y ampliarlos. Y es muy fácil subestimar la dificultad de escribir un analizador sin errores (incluso uno "trivial") desde cero.

+0

@stereofrog - Perdón, leí mal el historial de cambios. El enlace al menos debería funcionar ahora, aunque sé que no coincide con el formato preferido (por algún motivo, los enlaces entre corchetes no se muestran correctamente en Chrome). – Peter

0

sí, necesitas el Tokenizer, o algo similar, pero es solo una parte de la historia. Un tokenizer (más comúnmente llamado "lexer") solo puede leer y analizar elementos de una expresión, pero no tiene medios para detectar que algo como "foo() + * bar)" no sea válido. Necesita la segunda parte, llamada parser, que podría organizar tokens en una especie de árbol (llamada "AST") o proporcionar un mensaje de error cuando no lo haga. Irónicamente, una vez que tienes un árbol, "eval" ya no es necesario, puedes evaluar tu expresión directamente desde el árbol.

Le recomendaría que escriba un analizador a mano porque es un ejercicio muy útil y muy divertido. Recursive descent parsers son bastante fáciles de programar.

+0

Acepto que escribir un analizador sintáctico a mano es divertido y forma parte de la capacitación básica para cualquier programador serio. Pero si trabajas "en el reloj", sería mejor que la OMI reutilizara un generador de analizadores de preexigir y pasaras parte del tiempo guardando el aprendizaje sobre (y jugando con) las definiciones de la gramática. – Peter

+0

Veo su punto, pero estoy en desacuerdo en el caso de los analizadores sintácticos en particular. En primer lugar, incluso si está interesado en aprender cómo funcionan las cosas, es probable que cometa algunos errores estándar en el camino hacia la construcción de un analizador que sea lo suficientemente robusto como para determinar si una cadena determinada de la Big Bad Web puede pasar con seguridad a PHP. eval() - y aunque no nos gusta la ignorancia, tampoco nos gustan las aplicaciones web vulnerables. En segundo lugar, no creo que sea posible usar un generador de analizadores sintácticos (incluso superficialmente) sin aprender algo nuevo y útil, incluso si en última instancia se decide por otro enfoque. – Peter

3

En lo que se refiere a la validación, las siguientes fichas de personajes son válidos:

operator: [/*+-] 
funcs: (a\(|b\() 
brackets: [()] 
numbers: \d+(\.\d+)? 
space: [ ] 

una validación simple entonces podría comprobar si la cadena de entrada coincide con cualquier combinación de estos patrones. Debido a que el token funcs es bastante preciso y que no entre en conflicto tanto con otras fichas, esta validación debe ser bastante estable w/o la necesidad de implementar cualquier sintaxis/gramática ya:

Sólo
$tokens = array(
    'operator' => '[/*+-]', 
    'funcs' => '(a\(|b\()', 
    'brackets' => '[()]', 
    'numbers' => '\d+(\.\d+)?', 
    'space' => '[ ]', 
); 

$pattern = ''; 
foreach($tokens as $token) 
{ 
    $pattern .= sprintf('|(?:%s)', $token); 
} 
$pattern = sprintf('~^(%s)*$~', ltrim($pattern, '|')); 

echo $pattern; 

si toda la cadena de entrada partidos contra el patrón basado en token, valida.Aún podría ser PHP sintácticamente mal, puesto que puede asegurarse de que sólo es construir sobre los tokens especificados:

~^((?:[/*+-])|(?:(a\(|b\())|(?:[()])|(?:\d+(\.\d+)?)|(?:[ ]))*$~ 

Si se construye el patrón de forma dinámica - como en el ejemplo - que es capaz de modificar sus fichas de idioma en otro momento en más fácilmente.

Además, este puede ser el primer paso para su propio tokenizador/lexer. La secuencia de token se puede pasar a un analizador que puede validarlo e interpretarlo sintácticamente. Esa es la parte user187291 wrote about.

Como alternativa a escribir un analizador completo de lexer +, y necesita validar la sintaxis, también puede formular su gramática basada en tokens y luego hacer una gramática de símbolo regex basado en la representación del token de la entrada.

Los tokens son las palabras que usa en su gramática. Tendrá que describir la definición de paréntesis y función más precisamente que en tokens, y el tokenizer debería seguir reglas más claras que token reemplaza a otro token. El concepto se describe en another question of mine. Utiliza expresiones regulares también para la formulación de la gramática y la validación de la sintaxis, pero aún no se analiza. En su caso, eval sería el analizador que está utilizando.

+0

[¿Simula la construcción del lenguaje de array php o analiza con expresiones regulares?] (Http://stackoverflow.com/questions/3267951/simulate-php-array-language-construct-or-parse-with-regexp/3268443#3268443) – hakre

0

Puede usar token_get_all(), inspeccionar cada token y cancelar en el primer token no válido.

0

La respuesta de hakre, utilizando expresiones regulares es una buena solución, pero es un poquito complicado. También manejar una lista blanca de funciones se vuelve bastante desordenado. Y si esto sale mal, podría tener un efecto muy desagradable en su sistema.

¿Hay algún motivo por el que no use el javascript 'eval'?

+0

I Necesito obtener algunos datos de la base de datos en la fórmula, así que no puedo usar JS. – hidarikani

Cuestiones relacionadas