简体   繁体   English

如何用 PHP 中的值替换字符串中的变量?

[英]How can I replace a variable in a string with the value in PHP?

I have string like this in database (the actual string contains 100s of word and 10s of variable):我在数据库中有这样的字符串(实际的字符串包含 100 个单词和 10 个变量):

I am a {$club} fan

I echo this string like this:我像这样回显这个字符串:

$club = "Barcelona";
echo $data_base[0]['body'];

My output is I am a {$club} fan .我的 output 是I am a {$club} fan I want I am a Barcelona fan .我希望I am a Barcelona fan How can I do this?我怎样才能做到这一点?

Use strtr .使用strtr It will translate parts of a string.它将翻译字符串的一部分。

$club = "Barcelona";
echo strtr($data_base[0]['body'], array('{$club}' => $club));

For multiple values ( demo ):对于多个值(演示):

$data_base[0]['body'] = 'I am a {$club} fan.'; // Tests

$vars = array(
  '{$club}'       => 'Barcelona',
  '{$tag}'        => 'sometext',
  '{$anothertag}' => 'someothertext'
);

echo strtr($data_base[0]['body'], $vars);

Program Output:程序输出:

I am a Barcelona fan.
/**
 * A function to fill the template with variables, returns filled template.
 *
 * @param string $template A template with variables placeholders {$variable}.
 * @param array $variables A key => value store of variable names and values.
 *
 * @return string
 */

public function replaceVariablesInTemplate($template, array $variables){

 return preg_replace_callback('#{(.*?)}#',
       function($match) use ($variables){
            $match[1] = trim($match[1], '$');
            return $variables[$match[1]];
       },
       ' ' . $template . ' ');
}

I would suggest the sprintf() function.我建议使用sprintf()函数。

Instead of storing I am a {$club} fan , use I am a %s fan , so your echo command would go like:与其存储I am a {$club} fan ,不如使用I am a %s fan ,因此您的echo命令将如下所示:

$club = "Barcelona";

echo sprintf($data_base[0]['body'],$club);

Output: I am a Barcelona fan输出:我是巴塞罗那球迷

That would give you the freedom of use that same code with any other variable (and you don't even have to remember the variable name).这将使您可以自由地将相同的代码与任何其他变量一起使用(您甚至不必记住变量名)。

So this code is also valid with the same string:因此,此代码对于相同的字符串也有效:

$food = "French fries";

echo sprintf($data_base[0]['body'], $food);

Output: I am a French fries fan输出:我是炸薯条迷

$language = "PHP";

echo sprintf($data_base[0]['body'], $language);

Output: I am a PHP fan输出:我是 PHP 爱好者

Edit: This answer still gets upvotes, so people need to be aware that there's a security vulnerability in the naive interpolation technique present in the below code snippets .编辑:这个答案仍然得到了赞成,所以人们需要注意下面的代码片段中存在的幼稚插值技术存在安全漏洞 An adversary could include arbitrary variables in the input string which would reveal information about the server or other data in the runtime variable register.攻击者可以在输入字符串中包含任意变量,这将揭示有关服务器的信息或运行时变量寄存器中的其他数据。 This is due to the way the general expression search is performed in that it finds any arbitrary variable name pattern, and then uses those variable names verbatim in the subsequent compact call.这是由于通用表达式搜索的执行方式,它找到任意变量名称模式,然后在随后的compact调用中逐字使用这些变量名称。 This causes clients to control server-side behavior similar to eval .这会导致客户端控制类似于eval的服务器端行为。 I'm leaving this answer for posterity.我将这个答案留给后代。


You are looking for nested string interpolation.您正在寻找嵌套字符串插值。 A theory can be read in the blog post Wanted: PHP core function for dynamically performing double-quoted string variable interpolation .可以在博客文章中阅读一个理论:用于动态执行双引号字符串变量插值的 PHP 核心函数

The major problem is that you don't really know all of the variables available, or there may be too many to list.主要问题是您并不真正了解所有可用的变量,或者可能有太多无法列出。

Consider the following tested code snippet.考虑以下经过测试的代码片段。 I stole the regex from Mohammad Mohsenipur .从 Mohammad Mohsenipur 那里偷了正则表达式

$testA = '123';
$testB = '456';
$testC = '789';
$t = '{$testA} adsf {$testB}adf 32{$testC} fddd{$testA}';

echo 'before: ' . $t . "\n";

preg_match_all('~\{\$(.*?)\}~si', $t, $matches);
if ( isset($matches[1])) {
    $r = compact($matches[1]);
    foreach ( $r as $var => $value ) {
        $t = str_replace('{$' . $var . '}', $value, $t);
    }
}

echo 'after: ' . $t . "\n";

Your code may be:您的代码可能是:

$club = 'Barcelona';
$tmp = $data_base[0]['body'];
preg_match_all('~\{\$(.*?)\}~si', $tmp, $matches);
if ( isset($matches[1])) {
    $r = compact($matches[1]);
    foreach ( $r as $var => $value ) {
        $tmp = str_replace('{$' . $var . '}', $value, $tmp);
    }
}
echo $tmp;
if (preg_match_all('#\$([a-zA-Z0-9]+)#', $q, $matches, PREG_SET_ORDER));
{
    foreach ($matches as $m)
    {
        eval('$q = str_replace(\'' . $m[0] . '\', $' . $m[1] . ', $q);');
    }
}

This matches all $variables and replaces them with the value.这匹配所有 $variables 并用值替换它们。

I didn't include the {}'s, but it shouldn't be too hard to add them something like this...我没有包含 {},但添加它们应该不会太难,像这样......

if (preg_match_all('#\{\$([a-zA-Z0-9]+)\}#', $q, $matches, PREG_SET_ORDER));
{
    foreach ($matches as $m)
    {
        eval('$q = str_replace(\'' . $m[0] . '\', $' . $m[1] . ', $q);');
    }
}

Though it seems a bit slower than hard coding each variable.虽然它似乎比硬编码每个变量要慢一些。 And it introduces a security hole with eval.它引入了 eval 的安全漏洞。 That is why my regular expression is so limited.这就是为什么我的正则表达式如此有限的原因。 To limit the scope of what eval can grab.限制 eval 可以抓取的范围。

I wrote my own regular expression tester with Ajax, so I could see, as I type, if my expression is going to work.我用 Ajax 编写了自己的正则表达式测试器,所以我可以在输入时看到我的表达式是否可以工作。 I have variables I like to use in my expressions so that I don't need to retype the same bit for each expression.我有我喜欢在我的表达式中使用的变量,因此我不需要为每个表达式重新键入相同的位。

I've found these approaches useful at times:我发现这些方法有时很有用:

$name = 'Groot';
$string = 'I am {$name}';
echo eval('return "' . $string . '";');
$data = array('name' => 'Groot');
$string = 'I am {$data[name]}';
echo eval('return "' . $string . '";');
$name = 'Groot';
$data = (object)get_defined_vars();
$string = 'I am {$data->name}';
echo eval('return "' . $string . '";');

Here is my solution:这是我的解决方案:

$club = "Barcelona";

$string = 'I am a {$club} fan';

preg_match_all("/\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", $string, $matches);

foreach ($matches[0] as $key => $var_name) {
    if (!isset($GLOBALS[$matches[1][$key]]))
        $GLOBALS[$matches[1][$key]] = 'default value';
    $string = str_replace($var_name, $GLOBALS[$matches[1][$key]], $string);
}

You can use a simple parser that replaces {$key} with a value from a map if it exists.您可以使用简单的解析器将 {$key} 替换为映射中的值(如果存在)。

Use it like:像这样使用它:

$text = templateWith('hello $item}', array('item' => 'world'...));`

My first version is:我的第一个版本是:

/**
 * Template with a string and simple map.
 * @param string $template
 * @param array $substitutions map of substitutions.
 * @return string with substitutions applied.
 */
function templateWith(string $template, array $substitutions) {
    $state = 0; // forwarding
    $charIn = preg_split('//u', $template, -1, PREG_SPLIT_NO_EMPTY);
    $charOut = array();
    $count = count($charIn);
    $key = array();
    $i = 0;
    while ($i < $count) {
        $char = $charIn[$i];
        switch ($char) {
            case '{':
                    if ($state === 0) {
                        $state = 1;
                    }
                break;
            case '}':
                if ($state === 2) {
                    $ks = join('', $key);
                   if (array_key_exists($ks, $substitutions)) {
                        $charOut[] = $substitutions[$ks];
                   }
                   $key = array();
                   $state = 0;
                }
                break;
            case '$': if ($state === 1) {
                        $state = 2;
                      }
                  break;
             case '\\':    if ($state === 0) {
                           $i++;
                           $charOut[] = $charIn[$i];
                       }
                 continue;
             default:
                 switch ($state) {
                    default:
                    case 0: $charOut[] = $char;
                        break;
                     case 2: $key[] = $char;
                        break;
                   }
         }
    $i++;
    }

    return join('', $charOut);
 }

Maybe the following snippet is (partly) usefull for someone.也许以下代码段(部​​分)对某人有用。

/**
 * Access an object property using "dot" notation
 *
 * @param  object  $object
 * @param  string|null  $path
 * @param  mixed  $default
 * @return mixed
 */
function xobject_get(object $object, $path, $default = null) {
    return array_reduce(explode('.', $path), function ($o, $p) use ($default) { 
        return is_numeric($p) ? $o[$p] ?? $default : $o->$p ?? $default; 
    }, $object);
}

/**
 * Access an array's property using "dot" notation
 *
 * @param  array  $array
 * @param  string|null  $path
 * @param  mixed  $default
 * @return mixed
 */
function xarray_get(array $array, $path, $default = null) {
    return array_reduce(explode('.', $path), function ($a, $p) use ($default) { 
        return $a[$p] ?? $default; 
    }, $array);
}

/**
 * Replaces placeholders from a string with object or array values using "dot" notation
 *
 * Example:
 * "The book {title} was written by {author.name}" becomes "The book Harry Potter was written by J.K. Rowling"
 *
 * @param  array|object  $data
 * @param  string  $template
 * @return string
 */
function render_template($data, string $template) {
    preg_match_all("/\{([^\}]*)\}/", $template, $matches); 
    $replace = [];
    foreach ($matches[1] as $param) { 
        $replace['{'.$param.'}'] = is_object($data) ? xobject_get($data, $param) : xarray_get($data, $param); 
    }
    return strtr($template, $replace);
}

You can use preg_replace_callback for getting a variable name like:您可以使用preg_replace_callback获取变量名称,例如:

$data_base[0]['body'] = preg_replace_callback(
    '#{(.*?)}#',
    function($m) {
        $m[1] = trim($m[1], '$');
        return $this->$m[1];
    },
    ' ' . $data_base[0]['body'] . ' '
);

Attention: This code I wrote is for class($this);注意:我写的这段代码是针对class($this); . . You can declare a variable into the class.您可以在类中声明一个变量。 Then use this code for detecting the variables and replace them like:然后使用此代码检测变量并将它们替换为:

<?php
    class a {

        function __construct($array) {
            foreach($array as $key => $val) {
                $this->$key = $val;
            }
        }

        function replace($str){
            return preg_replace_callback(
                '#{(.*?)}#', function($m) {$m[1] = trim($m[1], '$'); return $this->$m[1];},
                ' ' . $str . ' ');
        }

    }

    $obj = new a(array('club' => 3523));

    echo $obj->replace('I am a {$club} fan');

Output:输出:

I am a 3523 fan

Try the preg_replace PHP function.试试preg_replace PHP 函数。

<?php
    $club = "Barcelona";
    echo $string = preg_replace('#\{.*?\}#si', $club, 'I am a {$club} fan');
?>

For your case, honestly, I do not see a reason not to use eval :)对于你的情况,老实说,我看不出有理由不使用 eval :)
Here is some extra way to define your variables if they are too into your database:如果变量太进入数据库,这里有一些额外的方法来定义变量:

$my_variable_name = 'club'; //coming from database
$my_value = 'Barcelona'; //coming from database
$my_msg= 'I am a {$club} fan'; //coming from database

$$my_variable_name = $my_value;  // creating variable $club dinamically
$my_msg = eval("return \"$my_msg\";"); // eating the forbidden fruit
echo $my_msg; // prints 'I am Barcelona fan'

This code is fully tested and working with php 7. But if you allow your users to define such strings into your database, better don't do it.这段代码已经过全面测试,可与 php 7 一起使用。但如果您允许您的用户将此类字符串定义到您的数据库中,最好不要这样做。 You should run eval only with trusted data.您应该只使用受信任的数据运行 eval。

Something like this should solve your problem:这样的事情应该可以解决您的问题:

$club = "Barcelona";
$var = 'I am a {$club} fan';

$res = preg_replace('/\{\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/e', "$$1", $var);
echo "$res\n";

It's a one-line preg_replace .这是一个单行preg_replace

With PHP 5.5, /e modifier is deprecated.在 PHP 5.5 中,不推荐使用/e修饰符。 You can use a callback instead:您可以改用回调:

$club = "Barcelona";
$var = 'I am a {$club} fan';

$res = preg_replace_callback('/\{\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/',
                             create_function(
                                 '$matches',
                                 'extract($GLOBALS, EXTR_REFS | EXTR_SKIP); return $$matches[1];'),
                              $var);
echo "$res\n";

Note that this uses a hack of importing all global variables.请注意,这使用了导入所有全局变量的技巧。 This may not be exactly what you want.这可能不是您想要的。 Possibly using closures would be a better idea.可能使用闭包会是一个更好的主意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM