Search code examples
phpregexoffsetpreg-replace-callback

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);

Solution

  • 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);