protected static function tokenize($search, $replace, $id, &$xpath, &$stack, &$index)
{
// The search can return 0, 1 or more fund patterns.
$matches_count = \preg_match_all($search, $xpath, $matches, \PREG_SET_ORDER | \PREG_OFFSET_CAPTURE);
// \PREG_OFFSET_CAPTURE calculates offsets starting from the begining of the string $xpath.
// Rewriting $xpath from left (first matches) creates a mismatch between the calculated offsets
// and the actual ones. This problem can be avoided rewriting $xpath from right (last matches).
for ($i = $matches_count - 1; $i >= 0; --$i) {
$match = $matches[$i];
// The count of the groups must exclude the entire recognized string entry.
$groups_count = \count($match) - 1;
// $match[0] is the entire recognized string.
// $match[>0] are the captured groups.
// $match[n][0] is the actual string.
// $match[n][1] is the position of the string.
list($pattern, $pattern_pos) = $match[0];
$xpath = \substr_replace($xpath, "{{$id}{$index}}", $pattern_pos, \strlen($pattern));
$groups_values = [];
for ($ii = 1; $ii <= $groups_count; ++$ii) {
// 0 is the group value, 1 is the group position.
$groups_values[$ii] = $match[$ii][0];
}
$stack[$index] = [$replace, $groups_values, $pattern];
++$index;
}
}