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

3

5

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);
Poser answered 13/12, 2011 at 3:43 Comment(0)
P
1

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);
Poser answered 2/6, 2023 at 3:3 Comment(1)
Time to move that green tick, I reckon (rather than promoting a workaround).Burson
E
7

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.

Engstrom answered 9/4, 2015 at 10:28 Comment(3)
The index of the match in the string, not the number of previous matches.Poser
If by index you mean position, then it makes senseEngstrom
This is the correct answer to a different question.Burson
D
2

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

Desperado answered 13/12, 2011 at 3:47 Comment(0)
P
1

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);
Poser answered 2/6, 2023 at 3:3 Comment(1)
Time to move that green tick, I reckon (rather than promoting a workaround).Burson

© 2022 - 2024 — McMap. All rights reserved.