1

I got a formula string containing a specific character I want to replace with an integer and calculate. The formula comes from different users, the integer value from a database.

Examples:

$formula = 'x/100';
$formula = '(5*(x+20))';
...

I got an individual number, let's say 34 and use this as x in my formula.

How do I do this with PHP? Couldn't find a solution.

I tried to use a user function, but unfortuneately this seems not to work.

$number  = 34;
$formula = str_replace('x', $number, $formula);

$result = function () use ($formula) {
  echo round($formula);
};

echo $example();

It results "34" instead calculating "34/100".

Deen
  • 51
  • 13
  • str_replace() and https://stackoverflow.com/questions/1015242/how-to-evaluate-formula-passed-as-string-in-php/1015281#1015281 –  Feb 24 '19 at 20:58
  • Hi @tim. I'm sorry that you don't like this site, but please don't give technical answers in the comments section. That's not how it works. Thanks. – Lightness Races in Orbit Feb 24 '19 at 20:59
  • Hi @deen. When you say you "couldn't find a solution", why was that? What sort of approaches have you taken thus far? How close did you get? How are you parsing that expression, at present? How are you replacing tokens with the value 34? What specific problems did you encounter with those approaches? – Lightness Races in Orbit Feb 24 '19 at 21:00
  • Possible duplicate of [How to evaluate formula passed as string in PHP?](https://stackoverflow.com/questions/1015242/how-to-evaluate-formula-passed-as-string-in-php) –  Feb 24 '19 at 21:01
  • I edited my original post to show my way I thought I could solve this easly. – Deen Feb 24 '19 at 21:25

1 Answers1

1

Working on such plain text formulas written by users requires lot of work to parse given expression, tokenize, validate and finally execute by replacing the placeholders as needed. (x is a placeholder in this case).

From programming point of view, this kind of goal would be really nice playground to dive into the world of Abstract Syntax Trees and Lexical Analysis. Personally, I would take a look into doctrine/lexer before trying to build something complex. It can reduce the overall work you need to achieve your goal A LOT. Doctrine ORM uses doctrine/lexer to understand DQL queries written by users.

I would NOT recommend using a hacky solution based on str_replace, preg_replace and eval() which may result with so serious security issues. Users may write ANY crap as a forumla and as a developer, you always need to validate that first. Without having a list of rules for understanding the structure of given formula, validation is mostly impossible.

Update

To demonstrate it's flaws and dangers, a relatively-secure eval scenario would look like below.

Following piece of crap is only for demonstrating the problems with eval() rather than an actual solution to the question. DON'T USE IT!

$formula = '(12+5) * 2 / x';
/**
 * Strip out everything other than a dot, arithmetic operators, paranthesis 
 * and the letter x to use as placeholder from given formula
 */
$filteredExpression = preg_replace('/[^\(\)\+\-\.\*\/\d+\.x]/', '', $formula);
$actualFormula = str_replace('x', 1, $filteredExpression);
$result = eval('return ' . $actualFormula . ';');

This example is partially works but it still has following important problems:

  • Users can add parenthesis more than required or they can forget closing an opening parenthesis. This would cause a Parse error:

    PHP Parse error: syntax error, unexpected ')'..

  • Users can try to divide an arbitrary number to zero:

    PHP Warning: Division by zero in..

  • Users can try to use a comma , instead of dot . as decimal placeholder. This may end up with wrong/unexpected results.
  • Users can introduce exponentiation (**) or increment/decrement ++, -- operators anywhere intentionally or unintentionally which may also end up with wrong / unexpected results. (A more complex regex may help this one but it does not worth it)
  • Business (or you) may want to support some simple functions in the future such as SUM(), AVG(), MIN(), MAX() etc. What to do now? Even some of them partially achievable, the code which does the actual job will quickly start becoming a mess after this point.
edigu
  • 9,878
  • 5
  • 57
  • 80