I’m trying to remove eval from the following function. I tried with sprintf and ${} , but still cannot find a solution.
Here the function:
function parseDbString(string $value = 'Looking for a good {{ $pippo }}'){ $pippo='Pizza'; return preg_replace_callback('/{{(.*?)}}/', function($res) use ($pippo) { // $val=${trim($res[1])}; Returns "Undefined variable: $pippo" $val=@eval("return ".trim($res[1]).";"); // Returns "Looking for a good Pizza" return isset($val) ? $val : $res[0]; },$value); }
Advertisement
Answer
So, yes, eval()
is often detested as one of the highest order “evils” in php. In most cases, when a task lends itself to be solved by eval()
or variable-variables (which are basically poorly packaged arrays), this is a symptom of inappropriately stored/declared data and often the best course of action is a complete rethink.
To solve your isolated question without fundamentally rewriting the custom function, I’ll offer a lesser “evil” (but still an “evil” in my opinion because there are risks in its usage) — GLOBALS
& global
…
Code: (Demo)
function parseDbString(string $value = 'Looking for a good {{ $pippo }}'){ global $pippo; // declare $pippo as a global variable $pippo = 'Pizza'; return preg_replace_callback('/{{ $(.*?) }}/', function($m) use ($pippo) { echo "Global: " , $GLOBALS['pippo']; echo "n{$m[1]}n"; return $GLOBALS[$m[1]] ?? $m[0]; // null coalescing operator provides fallback },$value); } echo parseDbString();
Output:
Global: Pizza # <-- for demonstraton purposes pippo # <-- for demonstraton purposes Looking for a good Pizza # <-- desired output
…so why is this workaround a “bad idea”, well, imagine you have a string that contains {{ $db }}
— such a common variable name is likely to exists in your list of global variables. So if the {{ variable }}
in your string matches ANY of the variables in the global scope, you’re going to get faulty outcomes.
Now, what should you do? Just declare your $pippo
data in an array so that you have an associative relationship to leverage. (Demo)
function parseDbString(string $value = 'Looking for a good {{ $pippo }}'){ $lookup = ['pippo' => 'Pizza']; return preg_replace_callback('/{{ $(.*?) }}/', function($m) use ($lookup) { return $lookup[$m[1]] ?? $m[0]; // null coalescing operator provides fallback }, $value); } echo parseDbString();
Depending upon the amount of control you have over your input data, you can now afford to remove the $
before pippo
in your input string — which eliminates a few unnecessary characters here and there.
And if you are still reading, you can clean this whole thing up with strtr()
or str_replace()
. (Demo)
function parseDbString(string $value = 'Looking for a good {{ $pippo }}'){ $lookup = ['{{ $pippo }}' => 'Pizza']; // this can be extended all you like! return strtr($value, $lookup); } echo parseDbString();