antesoles
antesoles

Reputation: 683

Why is preg_match behaving differently to preg_replace (resulting in different matches) in php?

Given the following string and regular expression, the resulting behavior is something I don't understand. preg_match delivers what I am expecting while preg_replace doesn't make sense to me.

$string = 'aaa [Ticket#RS-123456] äüö [xxx] ccc ddd';
$re = '@(.*)?(\[Ticket\#)(.*)(\])(.*)?@siU'; 

What I finally need in this example is the string RS-123456 (or whatever string would be at this position). This string should match at the 3rd position ($3), if I don't completely misunderstand regular expressions.

preg_match($re, $string, $matches_pm);

Result (as expected):
Array(
    [0] => aaa [Ticket#RS-123456]
    [1] => aaa 
    [2] => [Ticket#
    [3] => RS-123456 // That's exactly what I would expect
    [4] => ]
)


$res_pr = preg_replace($re, "$3", $string);

Result (unexpected):
RS-123456 äüö [xxx] ccc ddd

I hope anyone can open my eyes and show me where my logical failure is hiding.

Upvotes: 2

Views: 296

Answers (1)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 627022

Both match the same text, but preg_match returns the first match only while preg_replace replaces the match (that is not the entire string) with Group 3 contents leaving äüö [xxx] ccc ddd in the resulting string.

Use

$re = '@(.*)(\[Ticket\#)(.*?)(\])(.*)@si';  

to get the same results with preg_match and preg_replace.

See the PHP demo.

However, preg_match is the preferred way here:

if (preg_match('@\[Ticket#\K[^]]+@i', $string, $matches_pm)) {
    echo $matches_pm[0];
}

See this PHP demo.

Pattern details

  • \[Ticket# - a literal [Ticket# substring
  • \K - match reset operator discarding the currently matched text
  • [^]]+ - 1 or more chars other than ]

Upvotes: 2

Related Questions