如何使用preg_replace实现具有条件的模板

时间:2015-02-11 15:17:09

标签: php regex preg-replace preg-match pcre

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

我有一个函数,它接受一个模板并用$ 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);

示例:

  

专业{name:ucfirtst}是{profession_name:strtoupper}

分为

  

专业名称是ACTOR

我正在尝试实施条件,因此可以制定这样的规则:

  

在条件之前使用{placeholdes}的文字。 [{total_found},带有{占位符}的TRUE文本,带有{占位符}的FALSE文本],带有{占位符}的条件后跟踪文本。

所以我的函数会从注册表中得到{total_found}值,并且如果它是true或false则会返回带有相应false或true-pattern的字符串。 我在正则表达式上非常糟糕,并且无法弄清楚如何用正则表达式来编写这个条件。 请帮帮我。

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

$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;最终结果将是«职业是演员。没有找到空缺。你镇上的演员»。

1 个答案:

答案 0 :(得分:1)

$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_callbackdo...while循环中的原因。另一个原因是允许链接函数。

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

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