Ry-
Ry-

Reputation: 225281

Access the offset of the current match in the callback function of preg_replace_callback()

How can I keep track of the current match’s offset from the start of the string in the callback of preg_replace_callback?

For example, in this code, I’d like to point to the location of the match that throws the exception:

$substituted = preg_replace_callback('/{([a-z]+)}/', function ($match) use ($vars) {
    $name = $match[1];

    if (isset($vars[$name])) {
        return $vars[$name];
    }

    $offset = /* ? */;
    throw new Exception("undefined variable $name at byte $offset of template");
}, $template);

Upvotes: 5

Views: 2248

Answers (3)

Ry-
Ry-

Reputation: 225281

As of PHP 7.4.0, preg_replace_callback also accepts the PREG_OFFSET_CAPTURE flag, turning every match group into a [text, offset] pair:

$substituted = preg_replace_callback('/{([a-z]+)}/', function ($match) use ($vars) {
    $name = $match[1][0];

    if (isset($vars[$name])) {
        return $vars[$name];
    }

    $offset = $match[0][1];
    throw new Exception("undefined variable $name at byte $offset of template");
}, $template, flags: PREG_OFFSET_CAPTURE);

Upvotes: 1

David Rodrigues
David Rodrigues

Reputation: 12562

You can match first with preg_match_all & PREG_OFFSET_CAPTURE option and rebuild your string, instead of using the default preg_replace method.

Upvotes: 2

eithed
eithed

Reputation: 4359

As the marked answer is no longer available, here's what worked for me to get current index of replacement:

$index = 0;
preg_replace_callback($pattern, function($matches) use (&$index){
    $index++;
}, $content);

As you can see we have to maintain the index ourselves with the use of out-of-scope variable.

Upvotes: 7

Related Questions