Applying several regular expressions with different callbacks meant chaining preg_replace_callback() calls, each re-scanning the whole subject — three patterns, three passes.
$out = preg_replace_callback_array([
'/{{(w+)}}/' => function ($m) { return $vars[$m[1]] ?? ''; },
'/@(w+)/' => function ($m) { return mention($m[1]); },
], $template);
Patterns are applied in the order given and each sees the output of the one before, which is the detail that matters: a callback whose replacement text could match a later pattern will have it matched. That is occasionally what you want and usually not, so order the patterns from most specific to least.