5

I was playing around with this very basic class to learn more about regexes. What I wanted to achieve is to grab the content from a testview file, grab the string exactly between {{EACH slides}} and {{#EACH}}. The regex now present in the class did just that, plus the variable name in the opening tag. I then wanted to use that string to loop 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 badly. The preg_replace_callback() will match the first opening-tag, and the last closing-tag of the second loop, and give a string 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 the change the regex for quite some time now, and I'm starting to see more and more logic in it... but I have yet not solved my problem. Is it even possible to go down this path? or do I need another solution for scanning multiple EACH loops in my testfile? I will probably soon be using some framework, but I like doing this stuff when I'm bored during the weekends...

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've been going further.. its working great, but it doesn't cover an each inside another each. It's a situation like this, which I think might 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've been wondering if there is a way to cover it in 1 regex, and then if the function that processes the outer each-loop sees another each inside, it will finish that 1 first. I'm totaly willing to build the functions myself, but it would be awesome to get some tips. Would you recommend separating the regex? to find {{each X}} and {{#each}} and fix it another way in php, or fix the answered Regex to be able to handle this?

2 Answers 2

3

Your regex can be fixed as

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

See the regex demo.

The main point here is that [^*] you used in your pattern matches any character but * while you need to match any 0+ characters between one string and the other. It can be achieved with .*? and s modifier.

Details:

  • {{each - a literal substring
  • \s+ - 1+ whitespaces
  • ([^{}]*) - Group 1: zero or more chars other than { and }
  • }} - a literal }} substring
  • (.*?) - Group 2: any 0+ chars, as few as possible (due to the lazy *? quantifier
  • {{#each}} - a literal {{#each}} substring.
Sign up to request clarification or add additional context in comments.

9 Comments

Tnx! I will have to look into this tomorrow, I want to fully understand it before I continue, but it seems to work
One remark: if after {{each there may be { or } before the closest }}, you should replace ([^{}]*) with (.*?).
Tnx! I have implemented it now. Can do each with 3 types of arrays, if, else if and else, vars and superglobals. But the problem with my patterns is that I can't build each-loops within another each loop, and no ifs within ifs etc. I'm afraid I have to separate the opening and closing patterns, and go a different way. But for my school projects.. if it works it works lol.
Or is there a way to fix it with recursive regexes? xD haven't looked into that yet
|
1

Replace your code with this one:

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

RegEx101 Demo

Comments

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.