The answer here is surprisingly simple. The first group does not correspond (on the first pass), not even in space. The second group is trying to match the space with the "bar", which, of course, fails. If there is something behind the HAS that matches, the engine will now roll back and expand the first gripper group to fit the space. But it works just fine as it is now (the second group can really be emtpy), so it just stays that way.
To create what you expect, try the following:
preg_replace('~foo(.*?)(bar)?_~', 'foo bar_', $m);
In your edit, you added another capture group. (. *) now 2. It matches to the end of the line, as you thought. So you are right on this, you just changed the example ^^
Johannes H.
source share