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.