From: tjhunt Date: Thu, 11 Dec 2008 10:33:57 +0000 (+0000) Subject: weblib: MDL-17606 Make the highlight function better, with unit tests. X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=9289e4c90e7b47f07bdc09b42acd3a4a58d4273d;p=moodle.git weblib: MDL-17606 Make the highlight function better, with unit tests. --- diff --git a/lib/simpletest/testweblib.php b/lib/simpletest/testweblib.php index 708b58e974..7c01b1aba5 100644 --- a/lib/simpletest/testweblib.php +++ b/lib/simpletest/testweblib.php @@ -54,5 +54,17 @@ class web_test extends MoodleUnitTestCase { $this->assertEqual($tl->code2utf8(0x7fd2).$tl->code2utf8(0x7fd2), format_text_email('

習習

',FORMAT_HTML)); } + + function test_highlight() { + $this->assertEqual(highlight('good', 'This is good'), 'This is good'); + $this->assertEqual(highlight('SpaN', 'span'), 'span'); + $this->assertEqual(highlight('span', 'SpaN'), 'SpaN'); + $this->assertEqual(highlight('span', 'span'), 'span'); + $this->assertEqual(highlight('good is', 'He is good'), 'He is good'); + $this->assertEqual(highlight('+good', 'This is good'), 'This is good'); + $this->assertEqual(highlight('-good', 'This is good'), 'This is good'); + $this->assertEqual(highlight('+good', 'This is goodness'), 'This is goodness'); + $this->assertEqual(highlight('good', 'This is goodness'), 'This is goodness'); + } } ?> diff --git a/lib/weblib.php b/lib/weblib.php index 16a8d152b7..1b8d7b58c2 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -2202,61 +2202,59 @@ function convert_urls_into_links(&$text) { * This function will highlight search words in a given string * It cares about HTML and will not ruin links. It's best to use * this function after performing any conversions to HTML. - * Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html * - * @param string $needle The string to search for - * @param string $haystack The string to search for $needle in - * @param int $case whether to do case-sensitive or insensitive matching. - * @return string - * @todo Finish documenting this function + * @param string $needle The search string. Syntax like "word1 +word2 -word3" is dealt with correctly. + * @param string $haystack The string (HTML) within which to highlight the search terms. + * @param boolean $matchcase whether to do case-sensitive. Default case-insensitive. + * @param string $prefix the string to put before each search term found. + * @param string $suffix the string to put after each search term found. + * @return string The highlighted HTML. */ -function highlight($needle, $haystack, $case=0, - $left_string='', $right_string='') { +function highlight($needle, $haystack, $matchcase = false, + $prefix = '', $suffix = '') { +/// Quick bail-out in trivial cases. if (empty($needle) or empty($haystack)) { return $haystack; } - //$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle); // bug 3101 - $list_of_words = $needle; - $list_array = explode(' ', $list_of_words); - for ($i=0; $i $word) { + if (strpos($word, '-') === 0) { + unset($words[$index]); + } else if (strpos($word, '+') === 0) { + $words[$index] = '\b' . preg_quote(ltrim($word, '+'), '/') . '\b'; // Match only as a complete word. + } else { + $words[$index] = preg_quote($word, '/'); } } - $list_of_words = implode(' ', $list_array); - $list_of_words_cp = $list_of_words; - $final = array(); - preg_match_all('/<(.+?)>/is',$haystack,$list_of_words); - - foreach (array_unique($list_of_words[0]) as $key=>$value) { - $final['<|'.$key.'|>'] = $value; + $regexp = '/(' . implode('|', $words) . ')/u'; // u is do UTF-8 matching. + if (!$matchcase) { + $regexp .= 'i'; } - $haystack = str_replace($final,array_keys($final),$haystack); - $list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp); - - if ($list_of_words_cp{0}=='|') { - $list_of_words_cp{0} = ''; - } - if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') { - $list_of_words_cp{strlen($list_of_words_cp)-1}=''; +/// Another chance to bail-out if $search was only -words + if (empty($words)) { + return $haystack; } - $list_of_words_cp = trim($list_of_words_cp); +/// Find all the HTML tags in the input, and store them in a placeholders array. + $placeholders = array(); + $matches = array(); + preg_match_all('/<[^>]*>/', $haystack, $matches); + foreach (array_unique($matches[0]) as $key => $htmltag) { + $placeholders['<|' . $key . '|>'] = $htmltag; + } - if ($list_of_words_cp) { +/// In $hastack, replace each HTML tag with the corresponding placeholder. + $haystack = str_replace($placeholders, array_keys($placeholders), $haystack); - $list_of_words_cp = "(". $list_of_words_cp .")"; +/// In the resulting string, Do the highlighting. + $haystack = preg_replace($regexp, $prefix . '$1' . $suffix, $haystack); - if (!$case){ - $haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack); - } else { - $haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack); - } - } - $haystack = str_replace(array_keys($final),$final,$haystack); +/// Turn the placeholders back into HTML tags. + $haystack = str_replace(array_keys($placeholders), $placeholders, $haystack); return $haystack; }