如何使用 preg_replace 条件实现模板

how to implement template with conditions with preg_replace

我正在尝试实现一个管理界面,管理员可以在其中创建网站元标记形成的高级规则。

我有一个函数,它接受一个模板并将其中的占位符替换为 $registry 中的值,并在需要时应用修饰符。

$registy = array( 'profession_name' => 'actor', 'total_found' => 100, );

$result = preg_replace_callback('/\{([^{}:]+)(:[^{}:]+)?\}/', function($matches) use ($registry) {

    $result = $registry[$matches[1]] ?: $matches[0];

    if (in_array($matches[2], array(':ucfirst', ':strtolower', ':strtoupper', ':lcfirst')))
    {
        $result = call_user_func(substr($matches[2], 1), $result);
    }

    return $result;
},  
$template);

示例:

Profession {name:ucfirtst} is {profession_name:strtoupper}

被分割成

Profession Name is ACTOR

我正在尝试实施条件,因此可以制定如下规则:

text with {placeholdes} before condition. [{total_found}, TRUE text with {placeholders}, FALSE text with {placeholders}], trailing text with {placeholders} after condition.

所以我的函数将从注册表中获取 {total_found} 值,并且根据它是真还是假将 return 字符串与相应的假或真模式。 我很不擅长正则表达式,不知道如何用正则表达式来写这个条件。 请帮助我。

更新: 具体的例子看起来像:

$template = 'Profession is {profession_name}. [{total_found}, {total_found} vacancies found, No vacancies found]. {profession_name} in your town';

在我的回调函数中,我会放置一个这样的条件

if ($matches[condition]) // i don't no how exactly 
  $result = $matches[true-condition];
else
  $result = $matches[false-condition]

所以如果 $registry['total_found'] = 100; 最终结果会是 «职业是演员。找到 100 个职位空缺。你镇上的演员»。 如果 $registry['total_found'] = 0; 最终结果是 «职业是演员。没有找到职位空缺。你镇上的演员»。

$available_functions = array('ucfirst', 'strtolower', 'strtoupper', 'lcfirst');
$registry = array( 'profession_name' => 'actor', 'total_found' => 100, );
$template = 'Profession {"is":strtoupper} {profession_name:strtoupper:lcfirst}. [{total_found}, {total_found} vacancies found, No vacancies found]. {profession_name} in your town';

$pattern = <<<'EOD'
~
(?=[[{])  # speed up the pattern by quickly skipping all characters that are not
          # a "[" or a "{" without to test the two branches of the alternation 
(?:
    # conditional
    \[
      { (?<if> [^}]+ ) } ,   
        (?<then> [^][,]* )
        (?:, (?<else> [^][]* ) )?
     ]
  |
     # basic replacement
     (?<!\[) # allow nested conditionals           
     {
        (?<var_name> [^{}:]+ )
        (?: : (?<func_name> [^{}:]+ ) )? # first function to apply
        (?: (?<other_funcs> : [^}]+ ) )? # allow to chain functions
     }
)  
~x
EOD;

$replacement = function ($m) use ($registry, $available_functions) {
    if (!empty($m['if'])) {
        $cond = $m['if'];

        # when the condition doesn't exist, the string is evaluated as it
        if (isset($registry[$cond]))
            $cond = $registry[$cond];

        return $cond ? $m['then'] : $m['else'];

    } else {
        $value = isset($registry[$m['var_name']]) ? $registry[$m['var_name']] : trim($m['var_name'], '"\'');  

        if (isset($m['func_name'])) {
            $func = trim($m['func_name']);

            if (in_array($func, $available_functions))
                $value = call_user_func($func, $value);
        }

        if (isset($m['other_funcs']))
            return '{\'' . $value . '\'' . $m['other_funcs'] . '}';

        return $value;
    }
};

$result = $template;

do {
    $result = preg_replace_callback($pattern, $replacement, $result, -1, $count);
} while ($count);

echo $result;

当条件满足时,该方法是先用好的分支替换,然后在分支内进行第二次替换。这就是 preg_replace_callback 处于 do...while 循环中的原因。另一个原因是允许链式函数。

请注意,除了速度和可读性方面的小幅改进外,该模式不需要使用特殊功能。

我添加了一些改进,例如默认行为(当在注册表中找不到某些内容时)、使用链式函数和文字字符串的能力。您显然可以根据需要将它们更改为脚。