I was tasked to update some conditions in an application. I have a data set to be evaluated, and it has been hard-coded in the application following way:
$arr = array(
'a' => 'apple',
'b' => 'orange',
'c' => 1,
'd' => 2,
'e' => 5,
'f' => 'green',
'g' => 'red',
'h' => 'yellow',
)
$res1 = ($arr['a'] == 'apple') ? TRUE : FALSE;
$res2 = (($arr['b'] == $arr['f']) && ($arr['c'] < $arr['d']) ? TRUE : FALSE;
$res3 = (($arr['e'] == '5') && $res2) ?TRUE : FALSE;
and so on...
It is a nightmare to maintain in many places.
What I am essentially looking for is to make some way to pass in query string to evaluate data. For start a simple formula could be defined as an array
$formula = ['a', '=', 'apple'];
function query($formula, $arr) {
switch ($formula[1]) {
case '=':
return ($arr[$formula[0]] == $formula[2]);
case '!=':
return ($arr[$formula[0]]!= $formula[2]);
case '>':
return ($arr[$formula[0]] > $formula[2]);
case '<':
return ($arr[$formula[0]] == $formula[2]);
}
}
This then could be extended and called recursively
$formula = [['a','=','apple'], 'AND', ['e','<','10']]
but what I am essentially looking for is to store formulas a a string, like:
"((([a]="orange") OR ([c]<"4")) AND ([g]="red"))"
where [ ] would identify array keys
or maybe something like in Excel
"AND(OR(IF('a'='orange'),IF('c'<4)),IF('g'='red'))"
Is there any clean solution to do this? I have an idea how to build whole library for it, maybe in the future.
I don't want to add new conditions to the code every time. They are already all over the application. It would be better to store it in configuration and extend or modify in one place.
Any help much appreciated.
1 Answer 1
So this is only quick solution, but works for me right now.
$arr = array('a' => 'red','b' => 'blue');
$formula = ['[a]', '=', 'red'];
If there is [a] in formula it will be treated as array key.
function query($formula, $arr) {
$query_operator=$formula[1];
if (is_array($formula[0])) {
//recursive call
$query_left = query($formula[0], $arr);
} else {
//extracting string between brackets
preg_match("/\[([^\]]*)\]/", $formula[0], $match);
$query_left = $match ? $arr[($match[1])] : $formula[0];
}
if (is_array($formula[2])) {
//recursive call
$query_right = query($formula[2], $arr);
} else {
//extracting string between brackets
preg_match("/\[([^\]]*)\]/", $formula[2], $match);
$query_right = $match ? $arr[($match[1])] : $formula[2];
}
switch ($query_operator) {
case '=':
return ($query_left == $query_right);
case '!=':
return ($query_left != $query_right);
case '>':
return ($query_left > $query_right);
case '<':
return ($query_left == $query_right);
case 'AND':
return ($query_left && $query_right);
case 'OR':
return ($query_left || $query_right);
}
}
In this solution it will work with formula like:
$formula = [['[a]', '=', 'red'], 'AND', ['[b]', '=', 'blue']];
It is not exactly what I wanted, but does the job, and is not so terrible (I hope). It needs some input checking and error handling, but this is just an example.
eval()
.