Page 1 of 1

having trouble with preg_match_all() [solved]

Posted: Tue Jul 24, 2007 12:01 pm
by s.dot

Code: Select all

if(preg_match_all("/(\{if(.+?)\}.+?\{\/if\})/ism", $src, $matches))
This code produces me a very nice nested array. Unfortunately, I'm having troubles iterating through this array using correct array indexes.

I'd think it was a PHP bug in my version, but I know how php ignorant I am. I've tried many for() and foreach() loops, many print_r()'s and many hours with a headache.

What is the correct way to loop through $matches and get the value of (.+?) in the regex, for each set of matches that preg_match_all() finds?

... I probably just need sleep.

Here's my current coding after hours of deleting, editing, & trying again.

Code: Select all

private function _replace_if($src)
{
	if(preg_match_all("/(\{if(.+?)\}.+?\{\/if\})/ism", $src, $matches))
	{
		//	echo '<pre>';
			print_r($matches);
		//	echo '</pre>';
		for($i = 0; $i < count($matches); $i++)
		{
			$statement = str_replace(array('(', ')'), '', $matches[2][$i]);
			if(eval("return $statement;"))
			{
				echo 'true';
				//$src = str_replace('{if(' . $statement . '}
			} else
			{
				echo 'false';
			}
		}
	}
}
If you can't tell, it's a primitive template engine. Any other ways to evaluate {if ..} {/if} without using regexes without preg_* would be appreciated also.

Posted: Tue Jul 24, 2007 12:29 pm
by stereofrog
It appears to me that you need preg_replace_callback here rather than preg_match_all and a loop.

Posted: Tue Jul 24, 2007 1:10 pm
by s.dot
Thanks, I will give that a try.

Posted: Tue Jul 24, 2007 1:56 pm
by s.dot
So, I've got this.

Code: Select all

private function _replace_if($src)
{
	if(preg_match("/(\{if\((.+?)\)\}.+?\{\/if\})/ism", $src))
	{
		preg_replace_callback("/(\{if\((.+?)\)\}.+?\{\/if\})/ism",
			create_function(
				'$matches',
				'if(eval("return $matches[2];")){ echo \'true\'; } else { str_replace(\'i\', \'X\', "\$src"); }'
			),
			$src
		);
		
	}
}
It seems I can't get the varialbe $src to be abled to be passed to str_replace. I either get undefined variable $src or I litterally get '$src'.

Posted: Tue Jul 24, 2007 2:37 pm
by stereofrog
Hm.... honestly I don't get what you're after exactly. Here's a small example of how this _could_ look like, hope it helps.

Code: Select all

$text = "
1
{if 10 > 5} foo {/if}
2
{if 10 > 50} bar {/if}
3
";

function _compile_if($x) {
	$cond = eval("return $x[1];");
	return $cond ? $x[2] : '';
}
$new_text = preg_replace_callback(
	"~{if(.+?)}(.+?){/if}~si",
	'_compile_if',
	$text);
	
echo $new_text; // prints 1 foo 2 3

Posted: Tue Jul 24, 2007 10:06 pm
by s.dot
Wow,

Finally got it with:

Code: Select all

private function _replace_if($src)
{
	if(preg_match("#\{if\((.+?)\)\}(.+?)\{/if\}#ism", $src))
	{
		$src = preg_replace_callback(
			"#\{if\((.+?)\)\}(.+?)\{/if\}#ism",
			create_function(
				'$matches',
				'$statement = eval("return $matches[1];");
				return $statement ? $matches[2] : \'\';'
			),
		$src);
	}
	
	return $src;
}
My trouble was with understanding how exactly preg_replace_callback() works. Thanks a lot stereofrog!!

Posted: Wed Jul 25, 2007 9:17 am
by s.dot
Question on how I can get nested ifs to work...

IE

Code: Select all

{if($foo == 1)}
   say hi
   {if($bar == 2)}
       say what's up
   {/if}
{/if}
You can see how the regex finds the first {/if} and stops.

Posted: Wed Jul 25, 2007 9:21 am
by superdezign
Tokenization, which is what you should have been using in the first place.

Posted: Wed Jul 25, 2007 9:27 am
by s.dot
I think I'm going to rewrite my whole templating engine thus far.
Need to study this tokenizing thing.

Posted: Wed Jul 25, 2007 9:30 am
by superdezign
scottayy wrote:I think I'm going to rewrite my whole templating engine thus far.
Need to study this tokenizing thing.
Hehe, okay. I just recently wrote a basic template engine for a website I'm producing. So you know, it was extremely difficult for me to find any resources that explained *how* to tokenize content. There are plenty that explain *what* tokenization is though. I had to figure it out on my own. Eventually, I'd like to write an entire tokenizer class, but I don't quite feel like putting in the work yet.

Posted: Wed Jul 25, 2007 9:33 am
by s.dot
I think I'm trying to rush through it. Instead I need to realize that this is a very important aspect of my project, and take my time with it.

Did you find any good examples or tutorials? I'm googling right now, but you may have found something good in your search that I might not find.

Posted: Wed Jul 25, 2007 9:37 am
by superdezign
Actually, no. I never found much of anything other than concepts and Delphi code, but I did read one professor's notes that mentioned recursion and suddenly, it just clicked.

Posted: Wed Jul 25, 2007 10:45 am
by stereofrog
scottayy wrote:Question on how I can get nested ifs to work...
Depends on what your conditions look like. If you don't allow arbitrary strings between {if ... and matching }, you can use something like below to handle nested constructs:

Code: Select all

$r_if = '~
	{if ([^{}]+) }
		(
			(. (?! {if ) ) *?
		)
	{/if}
~six';

while(preg_match($r_if, $text))
	$text = preg_replace_callback(
        $r_if,
        '_compile_if',
        $text);


If you're going to allow free-form syntax like php itself has, you'll need lexer and parser. While the former is trivial thanks to regular expressions, the latter requires some "advanced" skills, like understanding recursion, stacks, formal grammars and all that stuff.

The terms for further googling are: lexer, php lexer, yacc, lemon parser, php parser generator.

Hope this helps.