1

我正在制作一个模板系统,我可以在其中输入:

<t:category:item:parameter1:parameter2...>

并将其替换为文件中的文本。可选参数被放置在替换字符串中,如%1, %2...

到目前为止,我有这个:

$data = preg_replace_callback("/<t:([^:]+):([^>]+)>/",function($m) use (&$userdata) {
    static $fcache = Array();
    $parse = function($file) use (&$fcache,&$lang) {
        // parse the file if it's a new one. False indicates success, otherwise error message is returned
        if( !isset($fcache[$file])) {
            if( !file_exists("text/".$lang."/".$file.".html")) $lang = "en";
            if( !file_exists("text/".$lang."/".$file.".html")) return "<div class=\"alert\">ERROR: File ".$file." not found.</div>";
            $k = "";
            foreach(file("text/".$lang."/".$file.".html") as $l) {
                if( substr($l,0,1) == "|") $k = rtrim(substr($l,1));
                else $fcache[$file][$k] .= $l;
            }
        }
        return false;
    };
    $lang = $userdata && $userdata['language'] ? $userdata['language'] : "uk";
    list(,$file,$d) = $m;
    $params = explode(":",$d);
    $section = array_shift($params);
    if( $e = $parse($file)) return $e;
    if( !$fcache[$file][$section]) {
        $lang = "uk";
        if( $e = $parse($file)) return $e;
    }
    return preg_replace_callback("/%(\d+)/",function($i) use ($params) {
        return htmlspecialchars_decode($params[$i[1]-1]);
    },trim($fcache[$file][$section]));
},$data);

文本文件的格式为:

|key
replacement text
|otherkey
more text %1

无论如何,切入正题:如果其中一个参数本身就是替换字符串怎么办?例如,如果我想要一个像“快来拜访他!”这样的字符串怎么办?- 我希望它是这样的:

<t:person:visit:<t:grammar:pronoun_object_m>>

该文件将具有:

|visit
Come and visit %1 soon!

|pronoun_object_m
him

但是,当前函数会将参数作为文字,并且在短语末尾<t:grammar:pronoun_object_m会显示一个额外的内容:>

快来参观<t:grammar:pronoun_object_m!>

实际上会显示为:

前来参观

由于未解析的替换看起来像一个 HTML 标记......

我相当确定我需要一个递归正则表达式,但是我对它们的工作方式感到非常困惑。谁能解释我如何“递归”我的正则表达式以允许这样的嵌入参数?

4

1 回答 1

2

递归解决方案的问题是,它们不能很好地与preg_replace. 它们主要用于preg_match. 原因是您将只能访问在递归中重用的模式的最后(最内部)捕获。因此,即使preg_replace_callback在这里也无济于事。

这是另一种可能性:

<t:person:visit:<t:grammar:pronoun_object_m>>,你得到你提到的输出的原因是,你的正则表达式将匹配这个:

<t:person:visit:<t:grammar:pronoun_object_m>

(它不能走得更远,因为您>在占位符中不允许。)

有几种方法可以解决这个问题。对于初学者,您也可以在占位符中禁止<(不仅是):>

"/<t:([^:]+):([^<>]+)>/"

现在您的模式将始终只找到最里面的占位符。因此,您可以简单地preg_replace_callback反复调用您的电话,直到不再进行替换为止。如何找到这个?添加可选的第四个和第五个参数:

do
{
    preg_replace_callback("/<t:([^:]+):([^<>]+)>/", $function, $data, -1, $count);
} while($count);

我还建议(为了易读性)您在preg_replace_callback函数之外定义回调。

于 2012-10-16T21:00:03.957 回答