PHP - 用与字符串相同名称的变量替换字符串

6

因此,这个字符串是这样的:

"bla bla bla {VARIABLE} bla bla"

当我在函数中使用此字符串时,我希望将{VARIABLE}替换为$variable(或任何用{}字符包装的大写字符串)。$variable(和任何其他变量)将在该函数内定义。
我能做到这点吗?

是的,但这看起来像一个非常糟糕的模板引擎想法。 - halfdan
我不会称它为模板引擎。它只是网站的一小部分,但我希望任何其他模块/插件都能在字符串显示在屏幕上之前更改它的能力。 - Alex
尝试使用 {GLOBALS},你就准备好被黑客攻击了。 - bcosca
8个回答

13
$TEST = 'one';
$THING = 'two';
$str = "this is {TEST} a {THING} to test";

$result = preg_replace('/\{([A-Z]+)\}/e', "$$1", $str);

1
由于你的解决方案可以达到效果,/e 修饰符被认为是一种安全风险。它还会暴露全局变量。 - Bouke
我假设输入是可信的——也就是说,源字符串来自原始程序员而不是动态提供给站点访问者。如果是这种情况,风险得到了缓解。而且,如果它像所述的那样在函数内运行,它不会暴露任何全局变量。 - Alex Howansky
谢谢。是的,该字符串并不真正动态。但它可以被其他模块(脚本)更改。这些模块可能包含恶意代码,因此保护字符串免受它们的影响并不重要... - Alex
@bouke:你只使用字母字符是无法进行恶意活动的。所以它应该足够安全了。 - NikiC
@nikic,那像$dbuser或$dbpass这样的变量呢?它们将被处理。另一方面,如果代码位于函数中,由于函数的作用域,这将不成问题。 - Bouke
显示剩余2条评论

13

使用正则表达式查找所有替换项,然后迭代结果并进行替换。确保只允许曝露想要的变量。

// white list of variables
$allowed_variables = array("test", "variable", "not_POST", "not_GET",); 

preg_match("#(\{([A-Z]+?)\}#", $text, $matches);

// not sure the result is in [1], do a var_dump
while($matches[1] as $variable) { 
    $variable = strtolower($variable);

    // only allow white listed variables
    if(!in_array($variable, $allowed_variables)) continue; 

    $text = str_replace("{".$match."}", $$match, $text);
}

4

使用$$vars$GLOBALS都会带来安全风险。用户应该能够明确地定义可接受的标签列表。

以下是我能想到的最简单的通用单函数解决方案。我选择使用双括号作为标记分隔符,但你可以很容易地进行修改。

/**
 * replace_tags replaces tags in the source string with data from the vars map.
 * The tags are identified by being wrapped in '{{' and '}}' i.e. '{{tag}}'.
 * If a tag value is not present in the tags map, it is replaced with an empty
 * string
 * @param string $string A string containing 1 or more tags wrapped in '{{}}'
 * @param array $tags A map of key-value pairs used to replace tags
 * @param force_lower if true, converts matching tags in string via strtolower()
 *        before checking the tags map.
 * @return string The resulting string with all tags replaced.
 */
function replace_tags($string, $tags, $force_lower = false)
{
    return preg_replace_callback('/\\{\\{([^{}]+)\}\\}/',
            function($matches) use ($force_lower, $tags)
            {
                $key = $force_lower ? strtolower($matches[1]) : $matches[1];
                return array_key_exists($key, $tags) 
                    ? $tags[$key] 
                    : '';
            }
            , $string);
}

[编辑] 增加了force_lower参数

[编辑] 在use列表中添加了force_lower变量 - 感谢启动被拒绝的编辑的人。


4

在其他答案(特别是Bill Karwin和bouke)的基础上,我们继续探讨...

 class CurlyVariables {

  private static $_matchable = array();
  private static $_caseInsensitive = true;

  private static function var_match($matches)
  {
    $match = $matches[1];

    if (self::$_caseInsensitive) {
      $match = strtolower($match);
    }

    if (isset(self::$_matchable[$match]) && !is_array(self::$_matchable[$match])) {
      return self::$_matchable[$match];
    }

    return '';
  }

  public static function Replace($needles, $haystack, $caseInsensitive = true) {
    if (is_array($needles)) {
      self::$_matchable = $needles;
    }

    if ($caseInsensitive) {
      self::$_caseInsensitive = true;
      self::$_matchable = array_change_key_case(self::$_matchable);
    }
    else {
      self::$_caseInsensitive = false;
    }

    $out = preg_replace_callback("/{(\w+)}/", array(__CLASS__, 'var_match'), $haystack);

    self::$_matchable = array();

    return $out;
  }
}

Example:

echo CurlyVariables::Replace(array('this' => 'joe', 'that' => 'home'), '{This} goes {that}', true);


2

这将有效……

$FOO = 'Salt';
$BAR = 'Peppa';
$string = '{FOO} and {BAR}';
echo preg_replace( '/\{([A-Z]+)\}/e', "$$1", $string );

但这似乎是个可怕的想法。


2
$data_bas = 'I am a {tag} {tag} {club} {anothertag} fan.'; // Tests

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

echo strtr($data_bas, $vars);

1

以下是另一种解决方案,但我同意其他人对于你是否应该这样做持怀疑态度。

<?php

$string = "bla bla bla {VARIABLE} bla bla";
$VARIABLE = "foo";

function var_repl($matches)
{
  return $GLOBALS[$matches[1]];
}

echo preg_replace_callback("/{(\w+)}/", "var_repl", $string);

1

我很高兴找到了Bill Criswell的解决方案,但是是否也可以替换这样的变量:

string tmp = "{myClass.myVar}";

其中PHP代码可能是这样的:

class myClass
{
    public static $myVar = "some value";
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接