Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 7 of 7
  1. #1
    Regular Coder
    Join Date
    Mar 2005
    Location
    Brighton, UK
    Posts
    117
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Templates, dealing with nested tags?

    Hi,

    Iím trying to write a simple template parser for an application Iím building, but I am stuck on something that seems like it should be obvious, but I cant work it out for the life of me.

    Say I have a template that looks like this:

    Code:
    Some text here
    {loop:products}
    	Text
    {/products}
    Some more text
    I can easily write a function with preg_replace_callback that will find all the loops and repeat the contents the number of time, or insert data etc., what goes in the loop is irrelevant at the moment. The regex looks like this:

    Code:
    /{loop:(.*?)}(.*?){\/loop}/is
    Where Iím stuck is working out how to deal with something like this:

    Code:
    Some text here
    {loop:products}
    	Text
    {loop:prices}
    	Text
    {/ loop}
    {/ loop}
    Some more text
    Because the regex will return:

    Code:
    {loop:products}
    	Text
    {loop:prices}
    	Text
    {/ loop}
    I get the feeling this canít be done with preg_replace_callback, but can someone point me in the right direction on how to deal with this? Being able to nest tags seems a fundamental part of any template system, mark-up language etc. I just donít have a clue how to deal with it.

    Thanks in advance, any help will be MUCH appreciated.

    Jack

  • #2
    Regular Coder
    Join Date
    Mar 2005
    Location
    Brighton, UK
    Posts
    117
    Thanks
    0
    Thanked 0 Times in 0 Posts
    No one?

  • #3
    Regular Coder ralph l mayo's Avatar
    Join Date
    Nov 2005
    Posts
    951
    Thanks
    1
    Thanked 31 Times in 29 Posts
    Remove the last question mark to make the match greedy (matching characters up until the last instead of the first match) and I think you get what you want:

    /{loop.*?)}(.*){\/loop}/is

  • #4
    Regular Coder
    Join Date
    Mar 2005
    Location
    Brighton, UK
    Posts
    117
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Yeah, but that would screw up when it comes to this:

    Code:
    {loop:products}
    	Text
    {/products}
    
    {loop:users}
    	Text
    {/products}
    It wouldnt be able to treat them as separate loops, it would make that one match, rather than two.

  • #5
    fci
    fci is offline
    Senior Coder
    Join Date
    Aug 2004
    Location
    Twin Cities
    Posts
    1,345
    Thanks
    0
    Thanked 0 Times in 0 Posts
    ok, here is what I came up with.
    it's not a complete solution but i figure it is close enough to what you need that you can customize (i.e., set callback functions for what you want to do with the different type of blocks). only caveat is you'd want to have the left curly braces { to be htmlentities or it'd likely break.
    Code:
    <?php
    
    $str=<<<TPL
    Some text here
    {loop:products}
        Text
        {loop:prices1}
            Text
        {/ loop}
        asdfasdf
        {loop:prices2}
            Text
            {loop:prices3}
                Text
                {loop:prices4}
                    Text
                {/ loop}
            {/ loop}
        {/ loop}
    {/ loop}
    finally Some more text
    TPL;
    
    # fun with regex
    preg_match_all("/([^{]*?)?\{(\/ )?loop(:[^}]+)?\}([^{]*)?/im", $str, $matches);
    
    #print_r($matches);
    print '<pre>';
    
    $len = count($matches[0]);
    
    # assign better variable names
    $before_text =&$matches[1];
    $after_text  =&$matches[4];
    
    $start_loops =&$matches[3];
    $end_loops   =&$matches[2];
    
    $in_loop = -1;
    $depth   = 0;
    $loops   = array();
    $delimit = " - "; # used to see the indentation
    
    for ($x=0; $x<$len; $x++) {
        $txt = preg_replace("/\s+/", "", $before_text[$x]); // remove all whitepsace
        $is_before = !empty($txt);
        $txt = preg_replace("/\s+/", "", $after_text[$x]); // remove all whitepsace
        $is_after  = !empty($txt);
    
        if ($is_before) {
            print "before: $before_text[$x]\r\n";
        }
    
        // see if this is a start loop
        if ($start_loops[$x]!="") {
            
            array_push($loops, $x);
    
    
            $msg = ''; 
            
            // track the depth level of the loops
            if ($in_loop!=-1 || $depth>0) {
                $depth++;
                $msg = "(nested)";
            }
            
            # execute what you want here
            # for now just display what's going on
            print str_repeat($delimit, $depth);
            print "Starting loop $x at depth $x $msg\r\n";
    
            $in_loop   = $x; 
    
        // else it might be closing the loop
        } else if ($end_loops[$x]!="") {
            $b = array_pop($loops);
            # execute what you want here
            # for now just display what's going on
            print str_repeat($delimit, $depth);
            print "Closing loop at depth $depth, loop $b\r\n";
    
            $depth--;
        }
    
        if ($is_after) {
            print "after: $after_text[$x]\r\n";
        }
    
    }
    
    ?>
    Last edited by fci; 02-12-2006 at 05:11 PM.

  • #6
    Regular Coder ralph l mayo's Avatar
    Join Date
    Nov 2005
    Posts
    951
    Thanks
    1
    Thanked 31 Times in 29 Posts
    Yeah, forgot about that. I wrestled with the same problem back when I used a regex template engine. I'll try to remember what I did to sort it and comment later, but for the record the whole thing turned out to be just a gigantic hassle and I'm advise you to switch to an engine based on XML or DOM (the latter of which, http://codingforums.com/showthread.php?t=76691), because they do a lot of the work of sorting the tags into logical structures for you.

    edit: or woot, nevermind, a solution!

  • #7
    Regular Coder
    Join Date
    Mar 2005
    Location
    Brighton, UK
    Posts
    117
    Thanks
    0
    Thanked 0 Times in 0 Posts
    fci:

    cheers for the script, i donít have time to try it out right now, but i will have a look later on, see if i ca make sense of it. Thanks again, really is much appreciated.

    ralph l mayo:

    yeah, i did consider an xml based system, but it has to be compatible with php 4 and 5, and 4 requires an extension, i know thatís not really much of a problem, but im just trying to avoid it at the moment.


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •