-1

I'd love to highlight a function's use in a block of code. For example, take a look at the instance of fwrite() in this sample code:

Simple

A simple preg_replace, and I can highlight that function:

$sample = preg_replace("/fwrite\((.*)\)\;?/U", "<code>$0</code>", $sample);

However, in the event that the function contains nested parentheses, it gets trickier.

If the code sample, instead was:

More complex

... then the regex pattern won't know that the content inside the fwrite() function isn't the closing of the function.

Ben Coffin
  • 359
  • 2
  • 10
  • 2
    Can you include `$sample` so we can work with the actual string? We can't work with images. – user3783243 Jun 27 '18 at 19:31
  • Is it required that the semi-colon at the end is not part of the total match? – Patrick Q Jun 27 '18 at 21:00
  • This seems to be related to another question already answered here : https://stackoverflow.com/questions/20569306/how-to-write-a-recursive-regex-that-matches-nested-parentheses – Pigeo Jun 27 '18 at 19:34
  • @PatrickQ, No, it's not required. – Ben Coffin Jun 28 '18 at 16:43
  • @BenCoffin And do you have any control over the text/content of `$sample`? I ask because the reason you're updated test doesn't match is due to it not having a semi-colon after the function call. If you are able to add the semi-colon to this text, then I will update my answer to reflect a change that will work. If you are _not_, then your question becomes more complex you should see [this related question](https://stackoverflow.com/questions/546433/regular-expression-to-match-outer-brackets) instead. – Patrick Q Jun 28 '18 at 18:13
  • @PatrickQ, no .. i'm not able to change the sample text. – Ben Coffin Jun 28 '18 at 18:17
  • Then see the question that I just mentioned, along with the one linked by Pigeo – Patrick Q Jun 28 '18 at 18:49
  • @PatrickQ Thank you. I updated the question showing the solutions culled from those two links (for the convenience of others). – Ben Coffin Jun 28 '18 at 19:20

2 Answers2

1

In the second example, it is stopping at the first ) (parenthesis) because you have the U (ungreedy) flag on your expression. So instead of the default behavior of matching as much as possible, it is now "lazy" and matches as little as possible. To resolve this, simply remove the U flag.

Then we have to address the fact that your first example excludes the ; (semi-colon) from the match. This is because you have the ? quantifier which matches the preceding character 0 or 1 times in a lazy (as few as possible, including zero) manner. To get this behavior after removing the U flag, we have to add a second ? which then flips the default behavior from greedy to lazy.

Put these together and you should get this:

$sample = preg_replace("/fwrite\((.*)\)\;??/", "<code>$0</code>", $sample);

DEMO

Patrick Q
  • 6,289
  • 2
  • 23
  • 34
0

SOLUTIONS:

1) Counting opens & closes:

function highlightcode($fn, $sample){
    $fn = rtrim($fn, ")");
    if(!$pos = $start = strpos($sample, $fn)) return($sample); //not found
    $opens = 1; $pos += strlen($fn);
    while($pos < strlen($sample)){
        $char = substr($sample, $pos, 1);
        $opens += ($char == "(" ? 1 : ( $char == ")" ? -1 : 0));
        //echo "POS: $pos CHAR: $char OPENS: $opens<br />";
        if($opens < 1){ $end = $pos; break; }
        $pos++;
    }
    return(substr($sample, 0, $start) . "<code>" . substr($sample, $start, ($end - $start)) . "</code>" . substr($sample, $end));
}

echo highlightcode("eval()", $sample);

2) Regex:

function highlightcode($fn, $sample){
    $fn = rtrim($fn, "()");
    $pattern = '~' . $fn . '(?= ( \( (?: [^()]+ | (?1) )*+ \) ) )~x';
    if(!preg_match_all($pattern, $sample, $matches)) return( $sample );
    foreach($matches[1] as $m){
        $find = "{$fn}{$m}";
        $repl = "<code>{$find}</code>";
        $sample = str_replace($find, $repl, $sample);
    }
    return( $sample );
}

echo highlightcode("eval()", $sample);
Ben Coffin
  • 359
  • 2
  • 10