Omid
Omid

Reputation: 4705

How to use preg_match for extracting custom tags

i use this pattern '/(\{(\w+)\}(.*))?\{%(\w+)%\}((.*)\{\/(\w+)\})?/i' to extract tags from a template using preg_match function.

sample template:

<table id="middle" cellspacing="0px" cellpadding="0px">
    {middle}
    <tr>
        {left}<td>{%left%}</td>{/left}
        <td>{%middle%}{%content%}</td>
        {right}<td>{%right%}</td>{/right}
    </tr>
    {/middle}
</table>

how to make sure that start and end of each tag truly match it's name
in this example middle tag matches for both middle and content while it should just match middle tag

Upvotes: 1

Views: 1030

Answers (2)

Francois Deschenes
Francois Deschenes

Reputation: 24969

I think the best way to solve this problem would be do this in a few different steps.

First, you should use preg_replace_callback with /(?>{([^}]+)})(.*)?{\/\1}/sim as your regular expression. This will find the top level {tag}{/tag}. The $matches[2] will contain the content (without the tags) while $matches1 will contain the tag itself.

You should create a function that's called recursively so that in the callback, it's called again on $matches[2] so find the children {tags} just in case there are more. This is how you'll go through the tree.

Finally, you should make a third function that handles the {%tag%}. I would use preg_replace_callback again and make use of the switch statement to handle the tag name.

This should point you in the right direction.

EDIT: Here is a fully functional demo of what I described above:\

<?php

$content = <<<END
{a}
  {b}I like {%first%} {%last%} a {c}lot{/c}.{/b}
{/a}
END;

echo find_tags($content);

function find_tags($content)
{
  return preg_replace_callback('/(?>{([^}]+)})(.*)?{\/\1}/sim', 'find_tags_callback', $content);
}

function find_tags_callback($matches)
{
  // Find and process any children tag pairs.
  $matches[2] = find_tags($matches[2]);
  
  // Process the tags {%tag%}.
  $matches[2] = preg_replace_callback('/{%([^%]+)%}/sim', 'process_tags', $matches[2]);
  
  switch ( $matches[1] )
  {
    case 'a':
      $tag = 'div';
      
      break;
    case 'b':
      $tag = 'p';
      
      break;
    case 'c':
      $tag = 'b';
      
      break;
  }
  
  return '<'.$tag.'>'.$matches[2].'</'.$tag.'>';
}

function process_tags($matches)
{
  switch ( $matches[1] )
  {
    case 'first':
      return 'Francois';
      
      break;
    case 'last':
      return 'Deschenes';
      
      break;
  }
}

//

The resulting string will be: <div><p>I like Francois Deschenes a <b>lot</b>.</p></div>.

Upvotes: 1

TylerT
TylerT

Reputation: 87

1I'm confident, but not certain, that to make sure the enclosing tags({this}{/this}) match the data tags ({%this%}), you would need an accompanying if statement to test the returned strings.

I would use the preg_replace_callback function, like so:

<?php
$str = '<template contents>';
$newstr = preg_replace_callback(
'/(\{(\w+)\}(.*))?\{%(\w+)%\}((.*)\{\/(\w+)\})?/i', 
'check', //<-- the function to send matches to
$str);
function check($matches){
    if($matches[1] == $matches[2] && $matches[1] == $matches[3]){
        /*Do Work*/
        return ''; //new formatted string to send back to the $newstr var
    }
}
?>

the preg_replace_callback function sends any matches found as an array to the specified function for processing, then you return the new formatted string from that function.

Upvotes: 0

Related Questions