【问题标题】:Replacing Tags with Includes in PHP with RegExps用 RegExps 替换 PHP 中包含的标签
【发布时间】:2009-04-06 20:55:31
【问题描述】:

我需要读取一个字符串,检测一个 {VAR},然后执行一个 file_get_contents('VAR.php') 来代替 {VAR}。 “VAR”可以命名为任何名称,例如 TEST 或 CONTACT-FORM 等。我不想知道 VAR 是什么——不是要进行硬编码条件,而是只想看到一个大写字母数字标签花括号,只需执行 file_get_contents() 即可加载它。

我知道我需要使用 preg_match 和 preg_replace,但我在这方面的 RegExps 遇到了麻烦。

这有什么用?它在挂钩 WordPress 时很有用。

【问题讨论】:

  • 小心允许 html 作者进行随机包含...这是一个很大的安全漏洞。
  • 我明白了。我将采取安全措施来保护 wp-admin,而 VAR 标签实际上将要求它以字母 X 开头(以免从 WordPress 中加载任何其他文件)并且除了字母数字短语外不支持任何内容之后。

标签: php regex preg-replace preg-match


【解决方案1】:

上面的 Orion 有一个正确的解决方案,但在您的简单情况下,实际上没有必要使用回调函数。

假设文件名是 A-Z + 连字符,您可以在正则表达式中使用 PHP 的 /e 标志在 1 行中完成:

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

这会将 {VAR} 的任何实例替换为 VAR.html 的内容。如果需要指定特定目录,可以在第二个术语中添加路径前缀。

存在与上述相同的模糊安全担忧,但我想不出任何具体的问题。

【讨论】:

  • 我的调整:$content = preg_replace('/\{([-A-Z0-9]+)\}/e','file_get_contents(TEMPLATEPATH .\'/hook-$1.php \')',$内容,1);这是因为在我的例子中,$content 中只有一个给定 {VAR} 的实例,而末尾的“1”使它运行得更快。
【解决方案2】:

您需要做很多事情。我假设您可以做一些工作来将要预处理的页面数据转换为字符串。

  1. 首先,您需要正确匹配正则表达式。使用/{\w+}/ 之类的东西应该相当容易。

  2. 接下来,您需要使用 preg_match 的所有标志来获取页面数据中的偏移位置。这个偏移量可以让你将字符串分成匹配的前、匹配和后部分。

  3. 获得这 3 个部分后,您需要运行包含,并将它们重新组合在一起。

  4. 起泡、冲洗、重复。

  5. 当您找不到更多变量时停止。

这不是非常有效,并且可能有更好的方法。您可能希望考虑做一个 preg_split,在 /[{}]/ 上拆分。无论您如何分割它,您都假设您可以信任您的传入数据,这将大大简化整个过程。为此,我会像这样布置代码:

  1. 获取您的内容并像这样拆分它:$parts = preg_split('/[{}]/', $page_string);

  2. 在具有以下条件的部分上编写递归函数:

    • 当 arg 的长度为
    • 否则,返回一个由
    • 组成的新数组
    • $arg[0]。加载数据($arg[1])。 $arg[2]
    • 加上 $argv[3...] 中剩下的任何内容
  3. 在 $parts 上运行您的函数。

【讨论】:

  • {} 应该在正则表达式中进行 \-转义。
  • preg_split,嗯?我会看看。
【解决方案3】:

你可以在没有正则表达式的情况下做到这一点(上帝保佑),比如:

//return true if $str ends with $sub
function endsWith($str,$sub) {
    return ( substr( $str, strlen( $str ) - strlen( $sub ) ) === $sub );
}

$theStringWithVars = "blah.php cool.php awesome.php";
$sub = '.php';
$splitStr = split(" ", $theStringWithVars);
for($i=0;$i<count($splitStr);$i++) {
    if(endsWith(trim($splitStr[$i]),$sub)) {
        //file_get_contents($splitStr[$i]) etc...
    }    
}

【讨论】:

  • 你认为哪个更快? preg_replace_callback() 还是你的 split/strlen/substr/trim 技术?
【解决方案4】:

在我的脑海中,你想要这个:

// load the "template" file
$input = file_get_contents($template_file_name);

// define a callback. Each time the regex matches something, it will call this function.
// whatever this function returns will be inserted as the replacement
function replaceCallback($matches){
  // match zero will be the entire match - eg {FOO}. 
  // match 1 will be just the bits inside the curly braces because of the grouping parens in the regex - eg FOO
  // convert it to lowercase and append ".html", so you're loading foo.html

  // then return the contents of that file.
  // BEWARE. GIANT MASSIVE SECURITY HOLES ABOUND. DO NOT DO THIS
  return file_get_contents( strtolower($matches[1]) . ".html" );
};
// run the actual replace method giving it our pattern, the callback, and the input file contents
$output = preg_replace_callback("\{([-A-Z]+)\}", replaceCallback, $input);

// todo: print the output

现在我将解释正则表达式

 \{([-A-Z]+)\}
  • \{\} 只是告诉它匹配花括号。您需要斜杠,因为 {} 是特殊字符,因此需要转义。
  • () 创建一个分组。基本上,这可以让您提取匹配的特定部分。我在上面的函数中使用它来匹配大括号内的内容,而不匹配大括号本身。如果我不这样做,那么我需要将 {} 从比赛中剔除,这会很烦人
  • [-A-Z] 表示“匹配任何大写字符,或 -
  • [-A-Z] 后面的 + 表示我们至少需要有 1 个字符,但我们最多可以有任何数字。

【讨论】:

  • 函数 replaceCallback($asMatches) { return file_get_contents(TEMPLATEPATH . '/hook-' . $asMatches[1] . '.php'); } $content = preg_replace_callback('/\{([A-Z0-9]+)\}/', replaceCallback, $content);
  • 这很漂亮,我喜欢。不过,我仍然对最初的概念感到相当不舒服:当心 {../../../../../etc/shadow} 和朋友。
  • 是的,我希望当我说 A-Z0-9 时,有人不能从 C 或汇编程序级别启动解析器,并让它开始接受 ../。 .等等。
【解决方案5】:

相对而言,正则表达式的开销较大。虽然您可能需要它们来确定要加载哪些文件,但您当然不需要它们来进行替换,并且可能不应该使用正则表达式。毕竟,您确切地知道要替换什么,那么为什么需要模糊搜索呢?

使用关联数组和 str_replace 进行替换。 str_replace 支持数组一次进行多次替换。一行替换,没有循环。

例如:

$substitutions = array('{VAR}'=>file_get_contents('VAR.php'), '{TEST}'=>file_get_contents('TEST.php'), ... ); $outputContents = str_replace(array_keys($substitutions), $substitutions, $outputContents);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-18
    • 1970-01-01
    • 1970-01-01
    • 2018-09-19
    • 2011-05-17
    • 2016-01-06
    • 1970-01-01
    相关资源
    最近更新 更多