13

Pretty straightforward; I can't seem to find anything definitive regarding PHP's preg_replace() supporting named backreferences:

// should match, replace, and output: user/profile/foo
$string = 'user/foo';
echo preg_replace('#^user/(?P<id>[^/]+)$#Di', 'user/profile/(?P=id)', $string);

This is a trivial example, but I'm wondering if this syntax, (?P=name) is simply not supported. Syntactical issue, or non-existent functionality?

Dan Lugg
  • 20,192
  • 19
  • 110
  • 174

4 Answers4

14

They exist:

http://www.php.net/manual/en/regexp.reference.back-references.php

With preg_replace_callback:

function my_replace($matches) {
    return '/user/profile/' . $matches['id'];
}
$newandimproved = preg_replace_callback('#^user/(?P<id>[^/]+)$#Di', 'my_replace', $string);

Or even quicker

$newandimproved = preg_replace('#^user/([^/]+)$#Di', '/user/profile/$1', $string);
Dimitry
  • 6,545
  • 2
  • 20
  • 21
7

preg_replace does not support named backreferences.

preg_replace_callback supports named backreferences, but after PHP 5.3, so expect it to fail on PHP 5.2 and below.

Thai
  • 10,746
  • 2
  • 45
  • 57
  • 1
    "Back references to the named subpatterns can be achieved by (?P=name) or, since PHP 5.2.2, also by \k or \k'name'. Additionally PHP 5.2.4 added support for \k{name} and \g{name}." This quote (from Dimitry's provided link) applies to `preg_match()` and `preg_match_all()` only, correct? – Dan Lugg Mar 10 '11 at 03:50
  • 1
    Yes, it only applies to the pattern. – Thai Mar 10 '11 at 04:49
2

preg_replace does not supported named subpatterns yet.

codaddict
  • 445,704
  • 82
  • 492
  • 529
0

you can use this :

class oreg_replace_helper {
    const REGEXP = '~
(?<!\x5C)(\x5C\x5C)*+
(?:
    (?:
        \x5C(?P<num>\d++)
    )
    |
    (?:
        \$\+?{(?P<name1>\w++)}
    )
    |
    (?:
        \x5Cg\<(?P<name2>\w++)\>
    )
)?
~xs';

    protected $replace;
    protected $matches;

    public function __construct($replace) {
        $this->replace = $replace;
    }

    public function replace($matches) {
        var_dump($matches);
        $this->matches = $matches;
        return preg_replace_callback(self::REGEXP, array($this, 'map'), $this->replace);
    }

    public function map($matches) {
        foreach (array('num', 'name1', 'name2') as $name) {
            if (isset($this->matches[$matches[$name]])) {
                return stripslashes($matches[1]) . $this->matches[$matches[$name]];
            }
        }
        return stripslashes($matches[1]);
    }
}

function oreg_replace($pattern, $replace, $subject) {
    return preg_replace_callback($pattern, array(new oreg_replace_helper($replace), 'replace'), $subject);
}

then you can use either \g<name> ${name} or $+{name} as reference in your replace statement.

cf (http://www.rexegg.com/regex-disambiguation.html#namedcapture)