1

A customer reported a bug and I've traced it to this code, but I can't figure out what is wrong with it:

$source = "This is a test.\n\n-- a <span style='color:red'>red word</span>!\n\n- a red word!\n\n";
//$find = "- a red word!";  // This one works!
$find = "- a <span style='color:red'>red word</span>!";  // This one doesn't...
$replace = "&bull; a <span style='color:red'>red word</span>!";
$pattern = '/^' . preg_quote($find) . '$/';
$results = preg_replace($pattern, $replace, $source);
die ("Results: " . serialize($results));            

I've included a sample of a $find that works vs a $find that doesn't work. Any idea why the uncommented $find doesn't work?

(Note: I'm not actually trying to parse HTML and the search is purely a sample, so I don't need corrections on the approach)

jacoz
  • 3,508
  • 5
  • 26
  • 42
Anthony
  • 5,275
  • 11
  • 50
  • 86
  • 1
    Please use properly [`preg_quote()`](http://php.net/manual/en/function.preg-quote.php), you **must** define the delimiter you're using, else the default value is `NULL`, so try `$pattern = '/^' . preg_quote($find, '/') . '$/';`. Also if you have turned on error reporting or checked the logs, you should have noticed something. – HamZa Jun 30 '13 at 21:30
  • 2
    Why not use `str_replace` ? – nice ass Jun 30 '13 at 21:30

3 Answers3

2

preg_quote doesn't escape the slash character found in </span> which makes the pattern invalid. preg_quote does permit defining the delimiter for the pattern:

$pattern = '/^' . preg_quote($find, '/') . '$/';
Joni
  • 108,737
  • 14
  • 143
  • 193
1

You must remove anchors (^ $) since what you try to match is only a substring, not all the string.

$pattern = '~' . preg_quote($find) . '~';
Casimir et Hippolyte
  • 88,009
  • 5
  • 94
  • 125
1

preg_quote escapes only special regular expression characters which are: . \ + * ? [ ^ ] $ ( ) { } = ! < > | : -. Because the forward slash is not a regexp special character, you must use a different delimiter, say colon sign |, in your pattern like this

$pattern = '/' . preg_quote($find) . '/'; 

or provide your back slash delimiter to the preg_quote function as a second parameter like this

$pattern = '/' . preg_quote($find, '/') . '$/';

From the PHP documentation on the preg_quote function (description of the second parameter):

If the optional delimiter is specified, it will also be escaped. This is useful for escaping the delimiter that is required by the PCRE functions. The / is the most commonly used delimiter.

And get rid of ^ and $, as was already suggested - you are not matching the whole string.

akhilless
  • 3,169
  • 19
  • 24