A browse through the Bugzilla source, and a bit of porting, turned up an idea.
Keep arrays of all the various bits that need to be worked on. One array for each type. In this case, there is just one, for $phpcodeblocks.
First, I'm going to "escape" # marks, so that I can use them as delimiters.
$text = str_replace('#','%#',$text);
Next, go through, finding bits we'll need to work on.
$count=0;
$placeholders = array();
$phpcodeblocks = array();
while(preg_match("/pattern to match things/", $text, $matches))
{ $placeholders[$count]="##$count##";
$text = preg_replace("/pattern to match things/",
$placeholders[$count],
$text,
1); // Not all at once!
$phpcodeblocks[$count++]=$matches[0];
}
(Bugzilla is written in Perl, which allows capturing bits of matched strings even as they're being replaced. PHP don't do that, though, which is why the pattern matching is done twice.) In this case, the pattern woul be much like the one I gave earlier. A faster one would be
#\[php\]((?!\[/php\]).)*\[/php\]#s
(The e modifier indicated that the replacement string is to be executed; it has no use in preg_match(), and we're not using it here in preg_replace() either.)
Now $phpcodeblocks is an array of all the bits in the text that we want to work over; in the original text they've been replaced by placeholders of the form ##\d+## (which is why we started by making sure such a pattern was otherwise impossible). We can work on each in splendid isolation.
When we're done, we copy them back in to the original. The reformatted $phpcodeblocks[0] will replace "##0##", $phpcodeblocks[1] will replace "##1##" and so on.
$text=str_replace($placeholders, $phpcodeblocks, $text);
And unescape the placeholder characters.
$text = str_replace('%#','#',$text);
I might go and try this out myself.
Of course, this was intended for situations where there was more than one type of block that needed to be reformatted - the idea being that the various modifications each one carried out didn't conflict with each other. WIth just one type of block, you can wrap all the reformatting code into a single function, and use an e-modified preg_replace() (like that of my previous post) to call it.