gudn tach!
Ansatz 1: Du benutzt den Limit-Parameter von preg_replace(). Leg ihn auf 1 fest und benutze preg_replace() in einer Schleife mit Zähler. So kannst du der Reihe nach die "Tokens" durch einen nummerierten String ersetzen.
in diesem zusammenhang koennte \G interessant sein, damit nicht immer der komplette string durchsucht werden muss.
\G sagt normalerweise z.b. in einer schleife, dass ab dort weitergesucht werden soll, wo eben (also beim letzten durchlauf) aufgehoert wurde.
ob/wie php damit zurechtkommt muesstest du mal ausprobieren. das manual macht widerspruechliche angabe zur existenz dieser funktionalitaet.
Ansatz 2: Du benutzt preg_match_all() Übergib der Funktion das zusätzliche Flag PREG_OFFSET_CAPTURE, und sie liefert dir zum Array der gefundenen Tokens noch die Positionen im zu durchsuchenden String mit. Mit dieser Information kannst du dir den Ergebnisstring ebenfalls in einer Schleife wieder neu basteln -- und dabei statt der gefundenen Tokens eben die mit dem Zähler wieder einbauen.
auf aehnliche weise koennte man auch via preg_split (je nach bedarf mit PREG_SPLIT_DELIM_CAPTURE), oder falls der "[xxx]"-kram auch ohne regexp findbar ist mit explode, vorgehen.
in anlehnung an wahsagas vorschlag mit preg_replace_callback waere auch ein einfacher aufruf von preg_replace mit dem e-modifier denkbar. letzteres ist vermutlich die kuerzeste (bezogen auf die quellcode-laenge) loesung:
$s='asd[xxx]bbdddb[xxx]asfhgsf[xxx]jhgfjsh[xxx]';
$i=0;
$s=preg_replace('/(\[xxx)(])/e',"'$1'.++\$i.'$2'", $s);
welches von diesen vielen moeglichkeiten nun die schnellste (in der ausfuehrung) ist, vermag ich jedoch nicht zu sagen.
prost
seth