问题
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);
}
回答1:
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();
回答2:
it work well with me after return value
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);
return $value;
}
but if you want it more dynamic you can use function below it you can pass $data as array like ['$pippo'=>'pizza'] and pass string in second parameter
function parseDbString2($data , $string) {
$parsed = preg_replace_callback('/{{(.*?)}}/', function ($matches) use ($data) {
list($shortCode, $index) = $matches;
if (isset($data[$index])) {
return $data[$index];
} else {
throw new \Exception("Shortcode {$shortCode} not found ", 1);
}
}, $string);
return $parsed;
}
hope it will help you
回答3:
Man you're tapping into some weird Perl-ish style of code. The problem is the double $$
that gets called by PHP. Once you trim one $
away it works.
<?php
function parseDbString(string $value = 'Looking for a good {{ $pippo }}'){
$pippo='Pizza';
return preg_replace_callback('/{{(.*?)}}/', function($res) use ($pippo) {
$val=${substr(trim($res[1]), 1)}; // here trim the $ away from the matched string
return isset($val) ? $val : $res[0];
},$value);
}
echo parseDbString(); // prints "Looking for a good Pizza"
Check the output of http://sandbox.onlinephpfunctions.com/code/86b3f37ac6c315d8e9a757c827455281df21fc89
回答4:
Replace {{}}
with another separator.
For example:
class Test {
protected $item1 = 'I`m item-1';
protected $item2 = 'I`m item-2';
public function parseDbString($value = 'Looking for a good 1:$$item1 2:$$item2 5:$$item5 blabla'){
$m = '';
$result = $value;
if( preg_match_all('~\$\$(.+?)\s~s', $value, $m)){
foreach( $m[1] as $var ){
if( property_exists( $this, $var )){
$result = str_replace('$$' .$var, $this->{$var}, $result);
} else {
$result = str_replace('$$' .$var, 'UNDEFINED', $result);
}
}
}
return $result;
}
}
$test = new Test();
var_dump( $test->parseDbString() );
来源:https://stackoverflow.com/questions/52853365/custom-parsing-function-php