discomatt Posted April 23, 2008 Share Posted April 23, 2008 I have a recursive function that parses template-like strings in loops. Here's a sample string {start->container} <some action here> <some more> {start->inner_left} <fill this with mysql data> {end->inner_left} {start->inner_right} <header stuff> {start->content} <inner stuff, more actions> {end->content) </header stuff> {end->inner_right} <footer> {end->container} And here's my function (and some supporting code) That finds these loops recursively. <?php # preg_recursive # $component - Used elsewhere in the script # $subject - Subject text for recursive 'callback' function preg_recursive ( $component, $subject ) { # \1 - Content before loop # \2 - Name of the loop # \3 - Content within loop # \4 - Content after loop $regex = '/((??!{start->).)*+){start->([\\w]++)}((??!{end->\\2).)++){end->\\2}(.*+)/s'; # If no matches, or error, return false if ( strpos($regex, '{start->') !== FALSE || ($count = preg_match_all($regex, $subject, $matches)) == 0 ) return FALSE; # Loop through matches for ( $i = 0; $i < $count; $i++ ) { # Check for further depth if ( ($inner = preg_recursive($component, $matches[3][$i])) === FALSE ) $inner = $matches[3][$i]; # Execute an outside function to further parse the results return parse($component .'-'. $matches[2][$i], $matches[1][$i] . $inner . $matches[4][$i]); # Remove the # below and comment line 26 (above) to test # return $matches[1][$i] .'{start->'. $matches[2][$i] .'}'. $inner .'{end->'. $matches[2][$i] .'}'. $matches[4][$i]; } } # Sample template string... will be loaded from outside file $template_string = <<<_TEMPLATE {start->container} <some action here> <some more> {start->inner_left} <fill this with mysql data> {end->inner_left} {start->inner_right} <header stuff> {start->content} <inner stuff, more actions> {end->content) </header stuff> {end->inner_right} <footer> {end->container} _TEMPLATE; # Call echo preg_recursive( 'someComponent', $template_string ); ?> Here's the unescaped regex ((??!{start->).)*+){start->([\w]++)}((??!{end->\2).)++){end->\2}(.*+) Is there any way to make this code more efficient? Or does it look fairly clean? Thanks in advance Quote Link to comment Share on other sites More sharing options...
effigy Posted April 23, 2008 Share Posted April 23, 2008 I do not get any results after switching the commented lines. Quote Link to comment Share on other sites More sharing options...
discomatt Posted April 23, 2008 Author Share Posted April 23, 2008 Ignore... making some changes (was late last night not sure where my logic was) Quote Link to comment Share on other sites More sharing options...
discomatt Posted April 23, 2008 Author Share Posted April 23, 2008 Here's a working version Sorry again <?php # preg_recursive # $component - Used elsewhere in the script # $subject - Subject text for recursive 'callback' function preg_recursive ( $component, $subject ) { # \1 - Centent before loop # \2 - Name of the loop # \3 - Content within loop # \4 - Content after loop $regex = '/((??!{start->).)*+){start->([\\w]++)}((??!{end->\\2).)++){end->\\2}(.*+)/s'; # If no matches, return unchanged string if ( strpos($subject, '{start->') === FALSE || ($count = preg_match_all($regex, $subject, $matches, PREG_SET_ORDER)) == 0 ) return $subject; # Loop through matches foreach ( $matches as $match ) { # Check for further depth $match[3] = preg_recursive( $component, $match[3] ); # Return results - would usually be calling a function here for further parsing return $match[1] .'{s->'. $match[2] .'}'. $match[3] .'{e->'. $match[2] .'}'. $match[4]; } } # Sample template string... will be loaded from outside file $template_string = <<<_TEMPLATE {start->container} <some action here> <some more> {start->inner_left} <fill this with mysql data> {end->inner_left} {start->inner_right} <header stuff> {start->content} <inner stuff, more actions> {end->content) </header stuff> {end->inner_right} <footer> {end->container} _TEMPLATE; # Call echo preg_recursive( 'someComponent', $template_string ); ?> Quote Link to comment Share on other sites More sharing options...
effigy Posted April 23, 2008 Share Posted April 23, 2008 I think this is more efficient--at least human-wise if not machine-wise--but benchmarks will tell the tale. It first grabs the offsets it needs to examine with a fixed pattern, then jumps straight to those. The patterns are much simpler and no recursion is involved. You could even change the offset examination portion to use a strpos loop. <pre> <?php $template_string = <<<_TEMPLATE {start->container} <some action here> <some more> {start->inner_left} <fill this with mysql data> {end->inner_left} {start->inner_right} <header stuff> {start->content} <inner stuff, more actions> {end->content} </header stuff> {end->inner_right} <footer> {end->container} _TEMPLATE; ### Gather starting offsets. preg_match_all('/{start->/', $template_string, $offsets, PREG_OFFSET_CAPTURE); echo '<b>Offsets:</b><hr/>'; print_r($offsets[0]); ### Check each one. foreach ($offsets[0] as $offset_arr) { $offset = $offset_arr[1]; echo "<hr/><b>Checking $offset</b>...<br>"; if (preg_match('/\G{start->(\w+)}.+?{end->\1}/s', $template_string, $matches, NULL, $offset)) { foreach ($matches as &$match) { $match = htmlspecialchars($match); } print_r($matches); } } ?> </pre> Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.