1

Here is my problem: I have a php file which contain this "html" code:

<div>
    {{ '{# Hi :D #}' }} {# Hello #}
    {{ $model }}
</div>

In my code I want to get the {# #} and the {{ }} to do different regex replacement but not the one that are in {{ }}.

The problem here is that if I do in the same (in PHP) preg_replace for the two possible match, this doesn't take the {{ }} found (That's what I want) but the replacement will be the same for the two:

/{{(.*?)}}|{#(.*?)#}/

This is logic but I want to do different replacements for each $1 and $2 match.

I thought to those possibilities :

  • First is to tell the regex to do not take the strings that are in the "{{ }}" parent but after multiple tentatives I failed to.
  • Second possibility is to add another replacement, in the preg_replace function you can do an array() replacement but this doesn't work like $1 = the first replacement and $2 = the second replacement, there is a replacement per regex request.

After 2 days trying to create this magic regex I finally ask a question in stackoverflow, hope someone will found the answer to this regex

Thanks,

6
  • Curly brackets are special characters in regex, used for specifying quantifiers. Make sure you're escaping them if you want to match them literally. Commented Sep 25, 2017 at 21:14
  • There is no differences if I escape them or not, I don't know if this is only a thing in PHP but well, it works escaped or not Commented Sep 25, 2017 at 21:19
  • Use preg_match_all, if in index 1 it is {{(.*?)}}, if in index 2 it is {#(.*?)#}. Commented Sep 25, 2017 at 21:43
  • Yeah sure but what if I want to keep the '{# Hi :D #}' in the {{ }}? I want to change the {# Hello #} but not the things in the {{ }} and what if I have multiple {# #}, the indexes doesn't work anymore Commented Sep 25, 2017 at 21:55
  • Could you post what your desired result is? I'm having trouble deciphering what "{# #} and the {{ }} to do different regex replacement but not the one that are in {{ }}" could possibly mean. You want x and y to do different things but not the ones that are in y. Sometimes you want y to do something and other times to not do something? That's gonna be a bit tough ;) Commented Sep 26, 2017 at 0:55

1 Answer 1

2

EDITED

Here's an approach using preg_split() with PREG_SPLIT_DELIM_CAPTURE flag in order to tokenize the string and account for nested tags.

$tokens = [
  // token definition as [open_tag, close_tag, replacement]
  ['{{', '}}', '<?php echo \1; ?>'],
  ['{#', '#}', '<?php //\1 ?>']
];

$open_tags = array_column($tokens, 0);
$close_tags = array_column($tokens, 1, 0); // mapped by open tag
$replacements = array_column($tokens, 2, 0); // mapped by open tag

$token_regex = '/(' . implode('|', array_map('preg_quote', $open_tags + $close_tags)) . ')/';

$parts = preg_split($token_regex, $input, -1, PREG_SPLIT_DELIM_CAPTURE);

$output = '';
while (null !== ($part = array_shift($parts))) {

  // open tag found...
  if (in_array($part, $open_tags)) {
    // ...start building string of full tag
    $tag_body = $part;
    // ...watch for corresponding close tag
    $close_at = [ $close_tags[$part] ];
    while (0 < count($close_at)) {
      $inner_part = array_shift($parts);
      if (in_array($inner_part, $open_tags)) {
        // nested tag found, add its closing to watchlist
        array_unshift($close_at, $close_tags[$inner_part]);
      } else {
        // close tag found, remove from watchlist
        if (reset($close_at) === $inner_part) {
          array_shift($close_at);
        }
      }
      $tag_body .= $inner_part;
    }

    // substitute full tag with replacement
    $tag_regex = '/^' . preg_quote($part) . '\s*(.*?)\s*' . preg_quote($close_tags[$part]) . '$/';
    $part = preg_replace($tag_regex, $replacements[$part], $tag_body);
  }

  $output .= $part;
}

echo $output;

You can try it out here.

Sign up to request clarification or add additional context in comments.

3 Comments

Well, it's a big step forward but what if you want to do {# {{ 'Hello' }} #}? This will transform to {# <?php echo 'Hello'; ?> #} whitout event taking care of the commentary's rule which is bad, I think the regex need to understand if there is something inside a parent regex. But the idea is here.
Do you also need to account for nested cases, such as {# {# 'Hello' #} #} or {{ '{{ Hi :D }}' }}?
Yup, that's exactly what I want

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.