Processo de equações matemáticas em php

Um usuário pode inserir qualquer equação matemática que eles gostem (com uma variável):

x + 5

1 - x/2

(x/3) * (56/13)

Estes são armazenados como strings no database. Quando são recuperados, preciso replace ‘x’ por um número e verificar o valor da equação.

Como eu poderia fazer isso?

Eu estava pensando em escrever um analisador para desconstruir as cordas e transformá-las em equações, no entanto, isso parece caro e problemático. A outra opção é passá-los através de eval (mas não sou um grande fã de usar eval se puder ajudá-lo).

Alguma ideia?

UPDATE: Eu também preciso ser capaz de obter o valor booleano de algo como “(x> 5)”. Isso não é possível com evalMath

UPDATE 2: Eu preciso triggersr muito disso um segundo. Eu estive olhando para o eval no php, mas não consigo retornar um booleano para (5> 4), no entanto, eu notei que js faria isso … talvez eu deveria investigar node.js …

UPDATE 3: Depois de se divertir tentando node.js (e conseguir que funcione), voltei e obtive eval para trabalhar em PHP, veja: O php eval pode retornar um valor booleano?

Então eu irei com o eval com um filtro muito muito hardcore na input do usuário.

Eval não é mau!

Sim, pode encher o sistema completamente se você escrever código ruim – mas as versões recentes do PHP podem analisar uma expressão inválida sem quebrar o script inteiro. E há muitas outras maneiras de expor o seu sistema escrevendo código incorreto.

Isso deixa a possibilidade de ataques de injeção de código – o que pode ser facilmente evitado fazendo um preg_replace em everythnig que não é um caractere seguro (ou seja, 0 …. 9, (,), +, -, *, /, ^, .)

Minha resposta padrão a esta questão sempre que surgir:

Não use eval (especialmente porque você está afirmando que isso é input do usuário) ou reinventar a roda escrevendo seu próprio analisador de fórmulas.

Dê uma olhada na class evalMath em PHPClasses. Deve fazer tudo o que você listou aqui.

EDITAR

re: Infelizmente, o evalMath não lida com coisas como (x> 5)

altere as linhas 177-179 para

 $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 

altere a linha 184 para

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

adicionar

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

após a linha 321

e evalMath agora lida com (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)')); 

Editar mais

Faltou a linha 307 que deveria ser modificada para ler:

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

Se você estiver lidando com a input do usuário, fico longe de eval. Escreva um analisador e quebre a fórmula em matrizes aninhadas.

 1 - x/2 

torna-se

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

É um pouco complicado escrever o analisador, mas é realmente fácil avaliar uma fórmula analisada.

Possibilidade ligeiramente arriscada se você estivesse executando seu código em uma checkbox do linux é usar o comando bc (certificando-se de escaping suas inputs corretamente antes de atribuí-lo ao sistema cmd). Eu não posso dizer que usar o sistema é muito melhor do que os riscos do eval, então espero alguns downvotes aqui.

Mesmo que você passe pelo eval, você terá que replace x por algum número. O tipo de estratégia que eu teria é passar o valor para x e ver qual é o valor avaliado. Se for mais de 0, então eu tentaria um número menor, e se for menor que 0, tentaria um número maior de forma recursiva até satisfazer uma margem de erro (<> 0.001%).

Depende …

Qual é a complexidade que aceitará? Porque, para equações matemáticas comuns (como as que você postou), não vejo muito problema ao escrever um analisador. A principal questão problemática seria redundar nos números e colocar parênteses corretos.

Mas se as equações vão aceitar inputs “avançadas”, como {[()]}, ou X², X³, ou ficando mais longe, cálculo diferencial e matemática da faculdade, então as coisas podem enlouquecer.

Se a complexidade atingir o tratamento simbólico, tente ler e pesquisar algo sobre o CAS (Calculate Algebra Systems).

Claro, eu recomendo que você crie seu próprio sistema para insumos, valide contra ele e evangelize os usuários para encheckboxr as inputs para ele. Nada muito complexo, mas o suficiente para tornar você (e outros) confortável e seguro para alcançar o que você precisa.

eval ()

Depende do que você precisa fazer, mas, de qualquer forma, a maneira mais barata de fazê-lo é usar uma function de substituição para as variables ​​e, em seguida, execute a expressão usando o eval ().
Claro que você precisa primeiro se certificar de que suas fórmulas estão na syntax php.
O bom é que você pode usar qualquer function matemática suportada por php, o que é ruim é que nunca é bom usar o eval () 🙂

PHPClasses

A outra boa opção é navegar na web até encontrar um analisador: P
http://www.phpclasss.org/package/2695-PHP-Safely-evaluate-mathematical-expressions.html

Usar a function eval é muito perigoso quando você não consegue controlar o argumento de string.

Experimente Matex para cálculos de fórmulas matemáticas seguras. Suporta também variables ​​e funções personalizadas.