skip to Main Content

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


  1. Chosen as BEST ANSWER

    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:

    {{s*(w*)s*}}(?=[^%]*%})
    

    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 block

    This is the final SINGLE LINE preg_replace() statement achieving the originally desired output:

    echo preg_replace('#{{s*(w*)s*}}(?=[^%]*%})#', '$$1', $string);
    

    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 :)


  2. 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)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search