I'm trying to implement an admin interface where manager could create advanced rules of site meta tags formation.
I have a function which takes a template and replaces placeholders in it with values from $registry and applies modifiers if needed.
$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);
Example:
Profession {name:ucfirtst} is {profession_name:strtoupper}
is parced to
Profession Name is ACTOR
I'm trying to implement conditions, so it would be possible to make rules like this:
text with {placeholdes} before condition. [{total_found}, TRUE text with {placeholders}, FALSE text with {placeholders}], trailing text with {placeholders} after condition.
So my function would get {total_found} value from registry and depending if it's true or false would return string with corresponding false or true-pattern.
I'm very bad at regular expressions, and can't figure out how to write this condition in regexp terms.
Help me please.
UPDATE:
concrete example looks like whis:
$template = 'Profession is {profession_name}. [{total_found}, {total_found} vacancies found, No vacancies found]. {profession_name} in your town';
in my callback function i would place a condition like this
if ($matches[condition]) // i don't no how exactly
$result = $matches[true-condition];
else
$result = $matches[false-condition]
So in case $registry['total_found'] = 100;
final result would be «Profession is actor. 100 vacancies found. Actor in your town».
In case $registry['total_found'] = 0;
final result would be «Profession is actor. No vacancies found. Actor in your town».
$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;
When a condition is met, the approach consists to replace with the good branch first and to proceed to the replacement inside the branch in a second time. This is the reason why preg_replace_callback
is in a do...while
loop. The other reason is to allow chained functions.
Note that except small improvements for speed and readability, the pattern doesn't need to use special features.
I have added several improvements like default behaviors (when something is not found in the registry), the ability to use chained functions and literal strings. You can obviously change them to feet to your needs.