I am working on a custom minimalist PHP templating engine for use in personal projects, I need to solve how to make some replacements using a regular expression. I have this string:
$string = 'sometext {% {{ var1 }} middletext {{ var2 }} %} moretext {{ varx }} {% {{ var3 }} %}';
I want to replace all variables between {{ }} delimiters with their php equivalent, but only if they are nested inside {% %} delimiters
This is the desired output:
sometext {% $var1 middletext $var2 %} moretext {{ varx }} {% $var3 %}
So far I have tried this:
echo preg_replace('#{{s*(.*?)s*}}#', '$$1', $string);
I have even tried this Regex with lookaheads:
echo preg_replace('#(?=.*{%?){{s*(.*?)s*}}(?=.*%})(?!=.*{%.*%})#', '$$1', $string);
But, in both cases I get the same output: All variables in the string are replaced irrespective of being inside {% %} delimiters or not (note how $varx is also undesirably replaced), my incorrect output is:
sometext {% $var1 middletext $var2 %} moretext $varx {% $var3 %}
I have solved it and gotten the output I desire by using foreach loops to match {% %} delimiters first and then replace the variables in the string by using arrays in preg_replace(), like this:
<?php
$string = 'sometext {% {{ var1 }} middletext {{ var2 }} %} moretext {{ varx }} {% {{ var3 }} %}';
$phpcode = preg_match_all('#{%.*?%}#', $string, $matches);
if ($phpcode) {
foreach ($matches as $match) {
if (is_array($match)) {
foreach ($match as $value) {
$replacements[] = preg_replace('/{{s*(.*?)s*}}/', '$$1', $value);
$patterns[] = "/" . preg_replace('/([[]{}.*+$/?^])/', '\$1', $value) . "/";
}
}
}
}
echo preg_replace($patterns, $replacements, $string);
the output can be tested here
But … I’m pretty sure theres a simple one-line of preg_replace() regex code that can achieve the same desired output. Any ideas?
2
Answers
Thanks for the advices and for the alternatives, I finally came up with a solution though. This is the Regular expression that worked for me:
Explanation:
{{s*(w*)s*}}
Matches anything inside the {{ }} delimiters including any number of leading or trailing blank spaces inside which makes the templating engine more forgiving(w*)
matches words with common variables naming convention (i.e. letters, digits or underscores), and puts it in a capture group(?=[^%]*%})
looks ahead to match ONLY if the condition is true (i.e. it matches only if the {{variable}} is nested inside the {% %} delimiters)[^%]*
matches each of the rest of the characters in the string to check that ARE NOT a % (percent character)...%}
matches the end of the {% %} code blockThis is the final SINGLE LINE preg_replace() statement achieving the originally desired output:
You can confirm the regex here and the replacements here
I tested this regex in my templating engine and I assert that I can now have any number of {{variables}} both inside and outside of {% ... %} code block delimiters :)
Here is a ready lib templates
https://github.com/H1l4nd0r/php_templates/tree/master
I created it several years ago, and still using it (inside it creates a tree and then builds it)