Backreference does not work in PHP

Lately I've been studying (more in practice to tell the truth) regex, and I'm noticing his power. This demand made by me (link), I am aware of 'backreference'. I think I understand how it works, it works in JavaScript, while in PHP not.

For example I have this string:

[b]Text B[/b]
[i]Text I[/i]
[u]Text U[/u]
[s]Text S[/s]

And use the following regex:

\[(b|i|u|s)\]\s*(.*?)\s*\[\/\1\]

This testing it on regex101.com works, the same for JavaScript, but does not work with PHP.

Example of preg_replace (not working):

echo preg_replace(
    "/\[(b|i|u|s)\]\s*(.*?)\s*\[\/\1\]/i", 
    "<$1>$2</$1>",
    "[b]Text[/b]"
);

While this way works:

echo preg_replace(
    "/\[(b|i|u|s)\]\s*(.*?)\s*\[\/(b|i|u|s)\]/i", 
    "<$1>$2</$1>",
    "[b]Text[/b]"
);

I can not understand where I'm wrong, thanks to everyone who helps me.


It is because you use a double quoted string, inside a double quoted string \1 is read as the octal notation of a character (the control character SOH = start of heading), not as an escaped 1.

So two ways:

use single quoted string:

'/\[(b|i|u|s)\]\s*(.*?)\s*\[\/\1\]/i'

or escape the backslash to obtain a literal backslash (for the string, not for the pattern):

"/\[(b|i|u|s)\]\s*(.*?)\s*\[\/\\1\]/i"

As an aside, you can write your pattern like this:

$pattern = '~\[([bius])]\s*(.*?)\s*\[/\1]~i';

// with oniguruma notation
$pattern = '~\[([bius])]\s*(.*?)\s*\[/\g{1}]~i';

// oniguruma too but relative:
// (the second group on the left from the current position)
$pattern = '~\[([bius])]\s*(.*?)\s*\[/\g{-2}]~i';