2010-10-25 14 views
8

Un utilisateur est autorisé à entrer toute équation mathématique qu'ils aiment (avec une variable):processus équations mathématiques en php

x + 5

1 - x/2

(x/3) * (56/13)

Ceux-ci sont stockés sous forme de chaînes de caractères dans la base de données. Quand ils sont récupérés, je dois remplacer «x» par un nombre et vérifier la valeur de l'équation.

Comment est-ce que je pourrais faire ceci? Je pensais écrire un analyseur pour déconstruire les chaînes et les transformer en équations, mais cela semble cher et problématique. L'autre option est de les faire passer par eval (mais je ne suis pas un grand fan d'utiliser eval si je peux l'aider).

Des idées?

MISE À JOUR: Je dois également pouvoir obtenir la valeur booléenne de quelque chose comme "(x> 5)". Ce n'est pas possible avec evalMath

MISE À JOUR 2: Je dois tirer lots par seconde. J'ai regardé dans eval en php mais ne peux pas l'obtenir pour renvoyer un booléen pour (5> 4) cependant j'ai remarqué que js le ferais ... peut-être je devrais étudier node.js ...

MISE À JOUR 3: Après avoir du plaisir d'essayer Node.js (et de le faire travailler) Je suis retourné et ai eval pour travailler en PHP voir: Can php eval return a boolean value?

Alors je vais aller avec eval avec un filtre très très hardcore sur l'entrée de l'utilisateur.

+0

Si vous avez besoin de faire des choses plus complexes, WolframAlpha offers an API pour les développeurs. – TheMagician

+0

@TheMagician Bonne idée, mais malheureusement, ils doivent être déclenchés très souvent (des milliers d'entre eux par seconde), donc une API externe ne va pas fonctionner. –

Répondre

4

Eval n'est pas mauvais !!!!!

Oui, il peut remplir complètement votre système si vous écrivez du mauvais code - mais les versions récentes de PHP peuvent analyser une expression invalide sans écraser le script entier. Et il existe de nombreuses autres façons d'exposer votre système en écrivant un mauvais code.Cela laisse juste la possiblité d'attaques par injection de code - ce qui peut facilement être évité en faisant un preg_replace sur everythnig qui n'est pas un caractère sûr (ie 0 .... 9, (,), +, -, *, /, ^,.)

+1

preg_replace pour rendre l'expression "safe" devient plus gênante si l'expression peut contenir des fonctions telles que log(), sin() ... OP n'indique pas si c'est le cas. –

+0

D'accord - c'est plus gênant - mais loin d'être impossible à mettre en œuvre. L'important est d'utiliser la liste blanche plutôt que la liste noire. – symcbean

+0

Merci pour une réponse Eval is not Evil :) –

0

Même si vous passez par eval, vous devrez remplacer x avec un certain nombre. Le type de stratégie que je voudrais faire est de passer la valeur pour x, et voir quelle est la valeur évaluée. Si elle est supérieure à 0, alors j'essaierais un plus petit nombre, et s'il est inférieur à 0, j'essaierais un plus grand nombre récursivement jusqu'à ce qu'il satisfasse une marge d'erreur (<> 0.001%).

-1

eval()

dépend de ce que vous avez à faire, mais de toute façon, le meilleur moyen de le faire est à l'aide d'une fonction Replace pour les variables puis exécutez l'expression en utilisant le eval().
Bien sûr, vous devez d'abord vous assurer que vos formules sont en syntaxe php.
La bonne chose est que vous pouvez utiliser une fonction mathématique pris en charge par php, la mauvaise chose est, il est jamais agréable d'utiliser le eval() :)

PHPClasses

L'autre bonne option, il surfe le web jusqu'à ce que vous trouviez un analyseur: P
http://www.phpclasses.org/package/2695-PHP-Safely-evaluate-mathematical-expressions.html

+2

-1 pour suggérer l'utilisation de eval() sur l'entrée de l'utilisateur – Hammerite

+2

-1 nice !!! C'est carrément dangereux avec l'entrée de l'utilisateur, comme cela a été dit comme une exigence dans cette question. –

+0

@Hammerite, @ Baker Baker: hé, j'écris que ce n'est jamais agréable d'utiliser l'eval ... – Cesar

0

... cela dépend

Quelle est la complexité qu'il acceptera? Parce que pour les équations mathématiques courantes (comme celles que vous avez publiées), je ne vois pas trop de problème à écrire un analyseur.La principale question problématique serait arrondir les nombres et placer la parenthèse correcte. Mais si les équations vont accepter des entrées "avancées", comme {[()]}, ou X², X³, ou obtenir des calculs différentiels et collégiaux, les choses peuvent devenir folles.

Si la complexité atteint une gestion symbolique, essayez de lire et de chercher quelque chose à propos de CAS (Calculate Algebra Systems).

Bien sûr, je vous recommande fortement de créer votre propre système pour les entrées, de le valider et d'évangéliser les utilisateurs pour leur associer des entrées. Rien de trop complexe, mais assez pour que vous (et les autres) soyez à l'aise et en sécurité pour atteindre ce dont vous avez besoin.

12

Ma réponse standard à cette question chaque fois qu'il affleure:

Ne pas utiliser eval (d'autant plus que vous déclarez que c'est entrée utilisateur) ou réinventer la roue en écrivant votre propre analyseur de formule. Regardez la classe evalMath sur les PHPClasses. Il devrait faire tout ce que vous avez énuméré ici.

EDIT

re: Malheureusement evalMath ne gère pas les choses comme (x> 5)

changement des lignes 177-179 à

$ops = array('+', '-', '*', '/', '^', '_', '>', '<', '='); 
$ops_r = array('+'=>0,'-'=>0,'*'=>0,'/'=>0,'^'=>0, '>' => 0, '<' => 0, '=' => 0); // right-associative operator? 
$ops_p = array('+'=>0,'-'=>0,'*'=>1,'/'=>1,'_'=>1,'^'=>2, '>' => 0, '<' => 0, '=' => 0); // operator precedence 

ligne de changement 184 à

if (preg_match("/[^\w\s+*^\/()\.,-<>=]/", $expr, $matches)) { // make sure the characters are all good 

ajouter

case '>': 
    $stack->push($op1 > $op2); break; 
case '<': 
    $stack->push($op1 < $op2); break; 
case '=': 
    $stack->push($op1 == $op2); break; 

après la ligne 321

et evalMath va maintenant gérer (x> 5), (x < 5) ou (x = 5)

// instantiate a new EvalMath 
$m = new EvalMath; 
$m->suppress_errors = true; 
// set the value of x 
$m->evaluate('x = 3'); 
var_dump($m->evaluate('y = (x > 5)')); 

De plus Modifier

Ligne manquée 307 qui doit être modifiée pour lire:

if (in_array($token, array('+', '-', '*', '/', '^', '>', '<', '='))) { 
+0

Malheureusement evalMath ne gère pas les choses comme (x > 5) –

+1

@ae - il n'y a pas beaucoup de travail à faire pour evalMath gérer (x> 5), etc. Il semblerait que vous préféreriez utiliser eval() malgré les dangers, mais voir mon edit pour les changements impliqué. –

+0

C'est génial! Je vais y réfléchir et voir comment ça se passe. –

1

Si vous traitez avec l'entrée de l'utilisateur je resterais loin de eval. Ecrivez un analyseur et divisez la formule en tableaux imbriqués.

1 - x/2 

devient

Array 
(
    [0] => - 
    [1] => 1 
    [2] => Array 
     (
      [0] =>/
      [1] => x 
      [2] => 2 
     ) 
) 

Il est un peu difficile à écrire l'analyseur, mais il est vraiment facile d'évaluer une formule analysable.

1

une possibilité légèrement risquée si vous étiez en train d'exécuter votre code sur une machine linux est d'utiliser la commande bc (en vous assurant d'échapper correctement vos entrées avant de la donner au système cmd). Je ne peux pas dire que l'utilisation du système est beaucoup mieux que les risques d'eval, donc j'attends des downvotes ici.

-1

L'utilisation de la fonction eval est très dangereuse lorsque vous ne pouvez pas contrôler l'argument de chaîne. Essayez le Matex pour le calcul des formules mathématiques sécurisées. Il prend également en charge les variables et les fonctions personnalisées.