繁体   English   中英

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

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

我在数据库中有这样的字符串(实际的字符串包含 100 个单词和 10 个变量):

I am a {$club} fan

我像这样回显这个字符串:

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

我的 output 是I am a {$club} fan 我希望I am a Barcelona fan 我怎样才能做到这一点?

使用strtr 它将翻译字符串的一部分。

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

对于多个值(演示):

$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);

程序输出:

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 . ' ');
}

我建议使用sprintf()函数。

与其存储I am a {$club} fan ,不如使用I am a %s fan ,因此您的echo命令将如下所示:

$club = "Barcelona";

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

输出:我是巴塞罗那球迷

这将使您可以自由地将相同的代码与任何其他变量一起使用(您甚至不必记住变量名)。

因此,此代码对于相同的字符串也有效:

$food = "French fries";

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

输出:我是炸薯条迷

$language = "PHP";

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

输出:我是 PHP 爱好者

编辑:这个答案仍然得到了赞成,所以人们需要注意下面的代码片段中存在的幼稚插值技术存在安全漏洞 攻击者可以在输入字符串中包含任意变量,这将揭示有关服务器的信息或运行时变量寄存器中的其他数据。 这是由于通用表达式搜索的执行方式,它找到任意变量名称模式,然后在随后的compact调用中逐字使用这些变量名称。 这会导致客户端控制类似于eval的服务器端行为。 我将这个答案留给后代。


您正在寻找嵌套字符串插值。 可以在博客文章中阅读一个理论:用于动态执行双引号字符串变量插值的 PHP 核心函数

主要问题是您并不真正了解所有可用的变量,或者可能有太多无法列出。

考虑以下经过测试的代码片段。 从 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";

您的代码可能是:

$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);');
    }
}

这匹配所有 $variables 并用值替换它们。

我没有包含 {},但添加它们应该不会太难,像这样......

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);');
    }
}

虽然它似乎比硬编码每个变量要慢一些。 它引入了 eval 的安全漏洞。 这就是为什么我的正则表达式如此有限的原因。 限制 eval 可以抓取的范围。

我用 Ajax 编写了自己的正则表达式测试器,所以我可以在输入时看到我的表达式是否可以工作。 我有我喜欢在我的表达式中使用的变量,因此我不需要为每个表达式重新键入相同的位。

我发现这些方法有时很有用:

$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 . '";');

这是我的解决方案:

$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);
}

您可以使用简单的解析器将 {$key} 替换为映射中的值(如果存在)。

像这样使用它:

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

我的第一个版本是:

/**
 * 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);
 }

也许以下代码段(部​​分)对某人有用。

/**
 * 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);
}

您可以使用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'] . ' '
);

注意:我写的这段代码是针对class($this); . 您可以在类中声明一个变量。 然后使用此代码检测变量并将它们替换为:

<?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');

输出:

I am a 3523 fan

试试preg_replace PHP 函数。

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

对于你的情况,老实说,我看不出有理由不使用 eval :)
如果变量太进入数据库,这里有一些额外的方法来定义变量:

$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'

这段代码已经过全面测试,可与 php 7 一起使用。但如果您允许您的用户将此类字符串定义到您的数据库中,最好不要这样做。 您应该只使用受信任的数据运行 eval。

这样的事情应该可以解决您的问题:

$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";

这是一个单行preg_replace

在 PHP 5.5 中,不推荐使用/e修饰符。 您可以改用回调:

$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";

请注意,这使用了导入所有全局变量的技巧。 这可能不是您想要的。 可能使用闭包会是一个更好的主意。

暂无
暂无

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

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