My bad, I should have tested it first (but I thought I'd take the Microsoft attitude of getting everyone else to beta test for me

)
The '!' were simply meant to be delimeters for the GREP pattern - I could have used any old character there.
The ?

b is meant to match a sub-pattern without actually including it in the captured result. So for instance with the following:-
preg_replace('!((?:HELLO)THERE)!msi','\\1','HELLOT HERE')
the search string matches 'HELLOTHERE' but only 'THERE' is captured and used as the replacement subpattern....
Actually, come to think of it, I didn't even need it in the example I gave up top. Ooops - sorry! The reason I was using it was to use the '\b' (or word boundary character) as a means of checking that the full word was matched, but not to include that boundary when it was replaced. What I SHOULD have written was:-
preg_replace('!?<=\b(".$censored.")?=\b!msi","**** ",$text);
...which uses assertions. It means that it should find a word boundary BEFORE ( ?<\b ) then the string you want to find, then another word boundary AFTER ( ?=\b ) but not to capture those word boundaries in the match.
The grep manual is full of very cool stuff - I just learned how to use the recursion modifier to match balanced tags within an RTF file, but it's taken me a week or so to fully get my head around it.
I can see why the true web geeks go for PERL, as it really does some powerful stuff - it's just that my poor brain is better suited to the less cryptic PHP for day-to-day stuff.