Jump to content

Efficiency Advice


discomatt

Recommended Posts

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 :)

Link to comment
https://forums.phpfreaks.com/topic/102478-efficiency-advice/
Share on other sites

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 );

?>

Link to comment
https://forums.phpfreaks.com/topic/102478-efficiency-advice/#findComment-525106
Share on other sites

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>

Link to comment
https://forums.phpfreaks.com/topic/102478-efficiency-advice/#findComment-525307
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.