1

This function searches for words (from the $words array) inside a text and highlights them.

function highlightWords(Array $words, $text){ // Loop through array of words
    foreach($words as $word){ // Highlight word inside original text
        $text = str_replace($word, '<span class="highlighted">' . $word . '</span>', $text);
    }         
    return $text; // Return modified text
}

Here is the problem:

Lets say the $words = array("car", "drive");

Is there a way for the function to highlight not only the word car, but also words which contain the letters "car" like: cars, carmania, etc.

Thank you!

webmasters
  • 5,663
  • 14
  • 51
  • 78

3 Answers3

1

What you want is a regular expression, preg_replace or peg_replace_callback more in particular (callback in your case would be recommended)

<?php
$searchString = "The car is driving in the carpark, he's not holding to the right lane.\n";

// define your word list
$toHighlight = array("car","lane");

Because you need a regular expression to search your words and you might want or need variation or changes over time, it's bad practice to hard code it into your search words. Hence it's best to walk over the array with array_map and transform the searchword into the proper regular expression (here just enclosing it with / and adding the "accept everything until punctuation" expression)

$searchFor = array_map('addRegEx',$toHighlight);

// add the regEx to each word, this way you can adapt it without having to correct it everywhere
function addRegEx($word){
    return "/" . $word . '[^ ,\,,.,?,\.]*/';
}

Next you wish to replace the word you found with your highlighted version, which means you need a dynamic change: use preg_replace_callback instead of regular preg_replace so that it calls a function for every match it find and uses it to generate the proper result. Here we enclose the found word in its span tags

function highlight($word){
    return "<span class='highlight'>$word[0]</span>";
}

$result = preg_replace_callback($searchFor,'highlight',$searchString);

print $result;

yields

The <span class='highlight'>car</span> is driving in the <span class='highlight'>carpark</span>, he's not holding to the right <span class='highlight'>lane</span>.

So just paste these code fragments after the other to get the working code, obviously. ;)

edit: the complete code below was altered a bit = placed in routines for easy use by original requester. + case insensitivity

complete code:

<?php

$searchString = "The car is driving in the carpark, he's not holding to the right lane.\n";
$toHighlight = array("car","lane");

$result = customHighlights($searchString,$toHighlight);

print $result;

// add the regEx to each word, this way you can adapt it without having to correct it everywhere

function addRegEx($word){
    return "/" . $word . '[^ ,\,,.,?,\.]*/i';
}

function highlight($word){
    return "<span class='highlight'>$word[0]</span>";
}

function customHighlights($searchString,$toHighlight){

// define your word list
$searchFor = array_map('addRegEx',$toHighlight);
$result = preg_replace_callback($searchFor,'highlight',$searchString);
return $result;

}
Harald Brinkhof
  • 4,375
  • 1
  • 22
  • 32
  • Ty very much, very interested... I have a funtion.php file. I'll copy the function there but not sure where to paste the other parts. – webmasters May 28 '12 at 20:52
  • I've added the complete code in 1 piece below for you to copy/paste :) – Harald Brinkhof May 28 '12 at 20:54
  • Ty very much, let me give it a try :) I am a seo, web developer and sometimes being helped by a programer really, really helps:) – webmasters May 28 '12 at 20:56
  • I want to test a way to put the functions in my functions.php file so I can use the code for different $searchString – webmasters May 28 '12 at 20:59
  • I've adapted the Complete Code part, so you could use it: put the routines in your functions file and then you can call customHighlights($searchString,$toHighlight); with obviously the searchString and the array words to highlight. Generally it's better/easier to keep them out of the function and in 1 file that you include. edit: I added it to the 'complete code' – Harald Brinkhof May 28 '12 at 21:07
  • You are great, ty very much...one more question if i can, will the code work regarding of caps? Like car, or Car? – webmasters May 28 '12 at 21:09
  • it will if you add the case-insensitive modifier to the regular expression so return "/" . $word . '[^ ,\,,.,?,\.]*/'; will need to become return "/" . $word . '[^ ,\,,.,?,\.]*/i'; (notice the i for insensitive search at the end? :) ) – Harald Brinkhof May 28 '12 at 21:11
0

I haven't tested it, but I think this should do it:-

$text = preg_replace('/\W((^\W)?$word(^\W)?)\W/', '<span class="highlighted">' . $1 . '</span>', $text);

This looks for the string inside a complete bounded word and then puts the span around the whole lot using preg_replace and regular expressions.

Purpletoucan
  • 6,472
  • 2
  • 21
  • 28
  • Let me test it, I am such a newb, would have never thought of this in a million years:) – webmasters May 28 '12 at 20:32
  • Beware, I just changed 'w' to 'W' to catch word boundaries! – Purpletoucan May 28 '12 at 20:33
  • Hmm, dreamweaver says there is something wrong with the php syntax – webmasters May 28 '12 at 20:34
  • @webmasters This will not work with single quotes, you will need to replace them with double quotes so the `$word` value gets used: `"/\W((^\W)?$word(^\W)?)\W/"`. – jeroen May 28 '12 at 20:42
  • 1
    Note: This regex is not UTF-8 aware - \W equates to [^a-zA-Z0-9_]. For a discussion of UTF-8 aware boundaries see http://stackoverflow.com/questions/4739880/how-can-i-match-a-russian-word-using-preg-replace-in-php/4750503#4750503 – tomwalsham May 28 '12 at 20:55
  • Here's an update, this time tested too, and using proper word boundaries so should be UTF8 aware, but I haven't followed @tomwalsham thread:- $text = preg_replace("/\b(\w*$word\w*)\b/", "$1", $text); – Purpletoucan May 28 '12 at 20:56
0
function replace($format, $string, array $words)
{
    foreach ($words as $word) {
        $string = \preg_replace(
            sprintf('#\b(?<string>[^\s]*%s[^\s]*)\b#i', \preg_quote($word, '#')),
            \sprintf($format, '$1'), $string);
    }
    return $string;
}


// courtesy of http://slipsum.com/#.T8PmfdVuBcE
$string = "Now that we know who you are, I know who I am. I'm not a mistake! It
all makes sense! In a comic, you know how you can tell who the arch-villain's
going to be? He's the exact opposite of the hero. And most times they're friends,
like you and me! I should've known way back when... You know why, David? Because
of the kids. They called me Mr Glass.";

echo \replace('<span class="red">%s</span>', $string, [
    'mistake',
    'villain',
    'when',
    'Mr Glass',
]);

Sine it's using an sprintf format for the surrounding string, you can change your replacement accordingly.

Excuse the 5.4 syntax

Dan Lugg
  • 20,192
  • 19
  • 110
  • 174
  • Ty very much. Will this work for caps, like pulling villain and Villain? – webmasters May 28 '12 at 21:07
  • @webmasters Yeop, case-insensitive (*that's the `i` modifier at the end of the expression `...\b#i`*) – Dan Lugg May 28 '12 at 21:12
  • Keep in mind; the `$format` argument requires "`%s`" somewhere to work, as `sprintf` replaces that token; as well, `$format` shouldn't contain "`$1`", or you might get funky results. – Dan Lugg May 28 '12 at 21:14