Page 1 of 1

Recursion in preg_replace

Posted: Mon Jun 06, 2005 11:45 am
by leenoble_uk
In developing a CMS system for a client I have developed an array of find/replace patterns to generate valid HTML and allow him to use limited custom tagging in a similar way to how this forum works.

Because this is for real world use I am trying to make it as accommodating as possible and I know that no matter how much instruction you give someone you cannot rely on them to follow them. So I've give him buttons which add tags like [bold] and [italic] to play with and I've written javascript at the client end which will automatically close any opened tags in the correct reversed order whenever the form is submitted or a tag is closed by clicking the wrong button. However, there are also [colour] tags. I know he is likely to play with these to begin with and I am trying to combat a scenario whereby he opens a load of colour tags to make rainbow writing but never switches off a colour before applying the next one.

Even after applying the javascript the raw text could contain something like this:

Code: Select all

їcolour-FF0000]here is my text how about a nice їcolour-FFCC00]rїcolour-FFFF00]a
їcolour-99FF00]iїcolour-009933]nїcolour-0066CC]bїcolour-FF66CC]oїcolour-660066]w
ї/colour-660066]ї/colour-FF66CC]ї/colour-0066CC]ї/colour-009933]ї/colour-99FF00]
ї/colour-FFFF00]ї/colour-FFCC00]ї/colour-FF0000]
I am assuming I need to use some kind of recursion in my pattern in order to catch all of the nested tags but I can't get it to work.

I tried this:

Code: Select all

$text = preg_replace("'\[colou?r-([A-F0-9]{6})\]((.*)?|(?R))\[/colou?r-\\1\]'", "<span style=\"color:#\\1\">\\2</span>", $text);
but it only catches the first colour, and everywhere else thatr I put the

Code: Select all

(?R)
I get an error message about the possibility of infinite recursion so it fails.

It appears I've reached the bounds of my REGEX capability. The only other way I could successfully get the desired result was to add an extra piece of code after the usual array replacement:

Code: Select all

for($i=0; $i<15; $i++)
	{
		$output = preg_replace("'\[colou?r-([A-F0-9]{6})\](.*)?\[/colou?r-\\1\]'","<span style=\"color:#\\1\">\\2</span>",$output);
	}
but this limits it to 15 layers rather than recurring for as long as necessary.

Help appreciated from those with knowledge of the dark arts.

Posted: Mon Jun 06, 2005 12:22 pm
by Skara

Code: Select all

'\&#1111;colou?r-(&#1111;A-F0-9]{6})\](.*?)\&#1111;/colou?r-\\1\]'
? goes inside the (). That should fix it.

hex colors can be 3 digits as well as 6, though. ;)

Posted: Mon Jun 06, 2005 12:22 pm
by Skara

Code: Select all

'\&#1111;colou?r-(&#1111;A-F0-9]{6})\](.*?)\&#1111;/colou?r-\\1\]'
? goes inside the (). That should fix it.

hex colors can be 3 digits as well as 6, though. ;)

Posted: Mon Jun 06, 2005 12:32 pm
by leenoble_uk
No, sorry. That had no effect whatsoever. It still only caught the first occurrence.

You removed the recurrence reference (?letterR) (forum replaces an R with are). I tried it both ways, with or without recurrence and I get the exact same result.

hmm. thanks for trying.

some time later....

Ok, for the time being I've had to kludge it by putting an extra cycle in after the usual find/replace routine.

Code: Select all

while($flag == false)
	{
		$oldOutput = $output;
		$output = preg_replace("'\[colou?r-([A-F0-9]{6})\](.*)?\[/colou?r-\\1\]'","<span style=\"color:#\\1\">\\2</span>",$output);
		$flag = $oldOutput == $output?true:false;
	}
That continues to replace the different colours until it can find no more but it's not the way I'd like to do it.