PHP regex, preg_replace_callback, with two possible matches in 1 line

I played with this very basic class to learn more about regular expressions. What I wanted to achieve was to grab the contents from the testview file, take the line exactly between {{EACH slides}} and {{#EACH}}. The regular expression present in the class did just that, plus the name of the variable in the opening tag. Then I wanted to use this line to encode it with the right variable and put it back in place.

But when I use 2 EACH loops in my view instead of 1, it fails. Preg_replace_callback () will match the first opening tag and the last closing tag of the second loop and give a line something like this:

"htmlbalalbal {{#EACH}} more htmlblabla {{EACH newsitems}} htmlblalblabla" 

From:

 <div class="rows-container"> {{each slides}} <div class="row flex-wrap" data-id="{{id}}"> </div> {{#each}} </div> <div class="rows-container"> {{each blogposts}} <div class="row flex-wrap" data-id="{{id}}"> </div> {{#each}} 

I’ve been trying to change the regex for a long time, and I'm starting to see more and more logic in it ... but I have not solved my problem yet. Is it even possible to go this way? or do I need another solution to scan multiple EACH loops in my test file? I'll probably use some framework soon, but I like to do this when I'm bored on the weekend ...

 class Aclass{ private $each = '~\{\{each ([^{]*)\}\}([^*]*)\{\{\#each\}\}~i'; private $current_file_content = ""; private $testviews = ['/testview.php']; private $views; function __construct($views){ $this->views = $views; //not in use $this->start(); } private function start(){ foreach($this->testviews as $file){ $this->current_file_content = file_get_contents(ROOT.$file); $this->scan_each(); } } private function scan_each(){ $result = preg_replace_callback($this->each, [$this, '_each'], $this->current_file_content); } private function _each($matches){ foreach($matches as $match){ var_dump(htmlspecialchars($match)); echo '<br><br>'; } } 

Edit: 16-5 So, I went further .. it works great, but it does not cover each of them inside each. This is a situation that, I think, may be useful in the future:

 {{each attributes}} <label>{title}</label> <select name="{key}" id="{key}" class="select"> <option value="0" selected>choose an option</option> {{each values}} <option value="{id}" {selected}>{title}</option> {{#each}} </select> {{#each}} 

I was wondering if there is a way to encompass it in 1 regex, and then if the function processing the external each loop sees another inside, it will finish this first. I am completely ready to build functions myself, but it would be nice to get some tips. Would you recommend splitting a regular expression? find {{every X}} and {{#each}} and fix it in another way in php or fix the Regex response to be able to handle this?

+5
source share
2 answers

Your regex can be fixed as

 '~{{each\s+([^{}]*)}}(.*?){{#each}}~is' 

See the demo of regex .

The main thing is that the [^*] that you used in your template matches any character, but * , while you need to match any 0+ characters between one line and another. This can be achieved using the modifier .*? and s .

More details

  • {{each is a literal substring
  • \s+ - spaces 1+
  • ([^{}]*) - Group 1: zero or more characters except { and }
  • }} - literal }} substring
  • (.*?) - Group 2: any 0+ characters, as small as possible (due to lazy *? Quantifier
  • {{#each}} is a literal substring {{#each}} .
+3
source

Replace the code with the following:

private $each = '~{{each\s+(.+?)}}(.+?){{#each}}~is';

Demo RegEx101

+1
source

All Articles