You need to use preg_replace_callback, with a pattern able to extract content between _{
and }
or ^{
and }
with nested curly brackets, and a callback function that will replace all occurrences of \dfrac
in the match. Example:
$pattern = '~[_^]({[^{}]*(?:(?1)[^{}]*)*})~';
$result = preg_replace_callback($pattern,
function ($m) { return str_replace('\dfrac', '\frac', $m[0]); },
$text);
pattern details:
~ # pattern delimiter
[_^] # _ or ^
( # open the capture group 1
{
[^{}]* # all that is not a curly bracket
(?: # open a non capturing group
(?1) # the recursion is here:
# (?1) refers to the subpattern contained in capture group 1
# (so the current capture group)
[^{}]* #
)* # repeat the non capturing group as needed
}
) # close the capture group 1
~
Note: if curly brackets are not always balanced, you can change quantifiers to possessive to prevent too much backtracking and to make the pattern fail faster:
$pattern = '~[_^]({[^{}]*+(?:(?1)[^{}]*)*+})~';
or you can use an atomic group as well (or better):
$pattern = '~[_^]({(?>[^{}]*(?:(?1)[^{}]*)*)})~';