I believe that for this you will need conditional subpatterns:
$query = "link"; $query = preg_quote($query, '/'); $p = '/((<)(?(2)[^>]*>)(?:.*?))*?(' . $query . ')/smi'; $r = "$1<strong>$3</strong>"; $str = '<a href="/Link/foo/the_link.htm">'."\n".'A Link</a>'; // multi-line text $nstr = preg_replace($p, $r, $str); var_dump( $nstr ); $str = 'Its not a Link'; // non-link text $nstr = preg_replace($p, $r, $str); var_dump( $nstr );
Output: (view source)
string(61) "<a href="/Link/foo/the_link.htm"> A <strong>Link</strong></a>" string(31) "Its not a <strong>Link</strong>"
PS: above regex also takes care of multi-line replacements and, more importantly, ignores the match not only of href, but of any other HTML object enclosed in < and > .
EDIT: If you just want to exclude hrefs and not all html objects, use this template instead of above in my answer:
$p = '/((<)(?(2).*?href=[^>]*>)(?:.*?))*?(' . $query . ')/smi';
source share