I am using this class to highlight the search keywords on a piece of text:
class highlight
{
public $output_text;
function __construct($text, $words)
{
$split_words = explode( " " , $words );
foreach ($split_words as $word)
{
$text = preg_replace("|($word)|Ui" ,
"<font style=\"background-color:yellow;\"><b>$1</b></font>" , $text );
}
$this->output_text = $text;
}
}
If
$text = "Khalil, M., Paas, F., Johnson, T.E., Su, Y.K., and Payer, A.F. (2008.) Effects of Instructional Strategies Using Cross Sections on the Recognition of Anatomical Structures in Correlated CT and MR Images. <i>Anatomical Sciences Education, 1(2)</i>, 75-83 "
which already contains HTML tags, and some of my search keywords are
$words = "Effects color"
The first look will highlight the word Effects, with <font style="background-color:yellow">Effect</font>, but the second loop will highlight the word color in the HTML tag. What should I do?
Is it possible to tell preg_replace to only highlight text when its not inside an alligator bracket?
Use a HTML parser to make sure that you only search through text.
You could use a CSS highlighted class instead and then use span tags, eg.
<span class="highlighted">word</span>
Then define your highlighted class in CSS. You could then exclude the word 'highlighted' from being valid in your search. Of course renaming the class to something obscure would help.
This also has the benefit of allowing you to change the highlight colour easily in the future, or indeed allowing the user to toggle it on and off by modifying the CSS.
Why use a loop?
function __construct($text, $words)
{
$split_words = preg_replace("\s+", "|", $words);
$this->output_text = preg_replace("/($split_words)/i" ,
"<font style=\"background-color:yellow; font-weight:bold;\">$1</font>" , $text );
}
A possible work-around would be to first wrap it with characters, which would (to 99%) not be a search input and replace those characters with the html tags after the 'foreach' loop:
class highlight
{
public $output_text;
function __construct($text, $words)
{
$split_words = explode(" ", $words);
foreach ($split_words as $word)
{
$text = preg_replace("|($word)|Ui", "%$1~", $text);
}
$text = str_replace("~", "</b></span>", str_replace("%", "<span style='background-color:yellow;'><b>", $text));
$this->output_text = $text;
}
}
Related
I'm having some troubles splitting a string by paragraphs.
I have a string inside $item->description, but it doesn't seems to be doing what I need.
My code:
$text = explode("</p>", $item->description);
var_dump($text);
It's outputing only the positions with all text, not doing the split:
array(1) { [0]=> string(726) "<b>Requirements</b> <p>A zeal towards learning to learn
as a priority. A sound knowledge of Internet and Browsing!</p> <b>Description</b> <p>The
course DIGITAL SKILLS FOR TEACHERS inherits the requirements for being a ROCKSTAR
Teachers with competency towards excellence within classrooms on narration with experience
and making a wow feature a continuous activity in classrooms on priority. The course
modules the expertise to imbibe for all teachers towards excellence with the adoption
of technology in a big very way!</p> <b>Who is the target audience?</b> <p>Any individual
who feels teaching is Sharing. A must for all Educators.</p>" }
Anyone could help me? Thanks!
Check this one :-
$text = $item->description ;
$arr = explode("</p>", $text);
echo "<br>".$arr[0] ;
Hope it will not come out with any issue .
If you want to get the plain text then you can use the strip_tags function. It will remove all the html tags from your text including paragraph tags.
For example:
/** Replace the <b> tag with placeholder */
$text = str_replace("<b>", "$", $item->description);
/** Replace the <\b> tag with placeholder */
$text = str_replace("<\b>", "^", $item->description);
/** Remove html from the text */
$text = strip_tags($text);
/** Replace placeholder with <b> tag */
$text = str_replace("$", "<b>", $item->description);
/** Replace placeholder with <\b> tag */
$text = str_replace("^", "<\b>", $item->description);
Alternately you can use the preg_match_all function. It will extract all text between paragraph tags. For example:
/** The text between paragraph tags is extracted using regular expression */
preg_match_all("/<p>(.*)<\/p>/iU", $item->description, $matches);
/** Each element of $text array contains text within a paragraph */
$text = $matches[1];
Use this format:-
$text = explode(PHP_EOL, $item->description);
Hope this will work .
You can use this code too :-
$text = preg_split('/\r\n|\r|\n/', $item->description) ;
echo $text[0] ;
Okay suppose we would like to get title,keywords and description of website so i'm going to use the following function
<?PHP
function getInfo($URL)
{
$getInfo= get_meta_tags($URL);
return $getInfo;
}
$URL = "http://www.my_site.com"; // URL
// Applying the function
$_getInfo = getInfo($URL);
// Print the results.
echo $_getInfo ["keywords"]."<br>"; // gives keywords
echo $_getInfo ["description"]."<br>"; // gives description
?>
Yet,everything if fine but suppose the results as following
Keywords
php,profitable,share
Description
Advanced profitable and featured script to share
As in this example we've some keywords found in description profitable and share
The question is how to highlight keywords that only found in description!!
I will add the following css
<style>
.highlight{background: #CEDAEB;}
.highlight_important{background: #F8DCB8;}
</style>
and will add this function to alter between two different colors just like in css code
<?PHP
function hightlight($str, $keywords = '')
{
$keywords = preg_replace('/\s\s+/', ' ', strip_tags(trim($keywords))); // filter
$style = 'highlight';
$style_i = 'highlight_important';
$var = '';
foreach (explode(' ', $keywords) as $keyword) {
$replacement = "<span class='".$style."'>".$keyword."</span>";
$var .= $replacement." ";
$str = str_ireplace($keyword, $replacement, $str);
}
$str = str_ireplace(rtrim($var), "<span class='".$style_i."'>".$keywords."</span>", $str);
return $str;
}
?>
Now applying both (Not working)
$string = hightlight($_getInfo["description"], $_getInfo ["keywords"]);
echo $string;
Why not working cause it define $_getInfo ["keywords"] as one word php,profitable,share
which indeed not found in description in that shape.
so how can i apply it by using explode or foreach (i guess) so the out put be like this :
I wonder if there was another way to do it if mine looks not good way. ~ Thanks
Since your keywords are in list format you need to:
foreach(explode(',', $keywords) as $keyword)
My code-
$input = "this text is for highlighting a text if it exists in a string. Let us check if it works or not";
$pattern ="/if/";
$replacement= "H1Fontbracket"."if"."H1BracketClose";
echo preg_replace($pattern, $replacement, $input);
Now the problem is that when i run this code, it splits into multiple lines, what else do i need to do so that i am able to get it in one line
Use str_replace rather than preg_replace. preg_replace will return an array of strings, and str_replace will just return the string:
echo str_replace($pattern, $replacement, $input);
What do you mean by multiple lines? Of course it'll show up as multiple lines on a webpage if you wrap the ifs in header tags. Headers are block elements. And more importantly, headers are headers. Not for highlighting text.
If you want to highlight something with HTML, you should probably use a span with a class, or you could use the HTML5 element mark:
$input = "this text is for highlighting a text if it exists in an iffy string.";
echo preg_replace('/\\bif\\b/', '<span class="highlighted">$0</span>', $input);
echo preg_replace('/\\bif\\b/', '<mark>$0</mark>', $input);
The \\b is to only match if words, and not just the if letters, which might be part of a different word. Then in your CSS you can decide how the marked words should show up:
.highlighted { background: yellow }
mark { background: yellow }
Or whatever. I would recommend that you read up a bit on how HTML and CSS works if you're going to make web pages :)
Try this
$input = "this text is for highlighting a text if
it exists in a string. Let us check if it works or not";
$pattern="if";
$replacement="<h1>". $pattern. "</h1>";
$input= str_replace($pattern,$replacement,$input);
echo "$input";
function highlight($str,$search){
$patterns = array('/\//', '/\^/', '/\./', '/\$/', '/\|/',
'/\(/', '/\)/', '/\[/', '/\]/', '/\*/', '/\+/',
'/\?/', '/\{/', '/\}/', '/\,/');
$replace = array('\/', '\^', '\.', '\$', '\|', '\(', '\)',
'\[', '\]', '\*', '\+', '\?', '\{', '\}', '\,');
$search = preg_replace($patterns, $replace, $search);
$search = str_replace(" ","|",$search);
return #preg_replace("/(^|\s)($search)/i",'${1}<span class=highlight>${2}</span>',$str);
}
I have a PHP highlighting function which makes certain words bold.
Below is the function, and it works great, except when the array: $words contains a single value that is: b
For example someone searches for: jessie j price tag feat b o b
This will have the following entries in the array $words: jessie,j,price,tag,feat,b,o,b
When a 'b' shows up, my whole function goes wrong, and it displays a whole bunch of wrong html tags. Of course I can strip out any 'b' values from the array, but this isn't ideal, as the highlighting isnt working as it should with certain queries.
This sample script:
function highlightWords2($text, $words)
{
$text = ($text);
foreach ($words as $word)
{
$word = preg_quote($word);
$text = preg_replace("/\b($word)\b/i", '<b>$1</b>', $text);
}
return $text;
}
$string = 'jessie j price tag feat b o b';
$words = array('jessie','tag','b','o','b');
echo highlightWords2($string, $words);
Will output:
<<<b>b</b>><b>b</b></<b>b</b>>>jessie</<<b>b</b>><b>b</b></<b>b</b>>> j price <<<b>b</b>><b>b</b></<b>b</b>>>tag</<<b>b</b>><b>b</b></<b>b</b>>> feat <<b>b</b>><b>b</b></<b>b</b>> <<b>b</b>>o</<b>b</b>> <<b>b</b>><b>b</b></<b>b</b>>
And this only happens because there are "b"'s in the array.
Can you guys see anything that I could change to make it work properly?
You problem is that when your function goes through and looks for all the b's to bold it sees the bold tags and also tries to bold them as well.
#symcbean was close but forgot one thing.
$string = 'jessie j price tag feat b o b';
$words = array('jessie','tag','b','o','b');
print hl($string, $words);
function hl($inp, $words)
{
$replace=array_flip(array_flip($words)); // remove duplicates
$pattern=array();
foreach ($replace as $k=>$fword) {
$pattern[]='/\b(' . $fword . ')(?!>)\b/i';
$replace[$k]='<b>$1</b>';
}
return preg_replace($pattern, $replace, $inp);
}
Do you see this added "(?!>)" that is a negative look ahead assertion, basically it says only match if the string is not followed by a ">" which is what would be seen is opening bold and closing bold tags. Notice I only check for ">" after the string in order to exclude both the opening and closing bold tag as looking for it at the start of the string would not catch the closing bold tag. The above code works exactly as expected.
Your base problem is that you quite wildly replace plain text strings inside HTML. That does cause your problem for small strings as you replace text in tags and attributes as well.
Instead you need to apply your search and replace to the text between HTML texts only. Additionally you don't want to highlight inside another highlight as well.
To do such things, regular expressions are quite limited. Instead use a HTML parser, in PHP this is for example DOMDocument. With a HTML parser it is possible to search only inside the HTML text elements (and not other things like tags, attributes and comments).
You find a highlighter for text in a previous answer of mine with a detailed description how it works. The question is Ignore html tags in preg_replace and it is quite similar to your question so probably this snippet is helpful, it uses <span> instead of <b> tags:
$doc = new DOMDocument;
$doc->loadXML($str);
$xp = new DOMXPath($doc);
$anchor = $doc->getElementsByTagName('body')->item(0);
if (!$anchor)
{
throw new Exception('Anchor element not found.');
}
// search elements that contain the search-text
$r = $xp->query('//*[contains(., "'.$search.'")]/*[FALSE = contains(., "'.$search.'")]/..', $anchor);
if (!$r)
{
throw new Exception('XPath failed.');
}
// process search results
foreach($r as $i => $node)
{
$textNodes = $xp->query('.//child::text()', $node);
// extract $search textnode ranges, create fitting nodes if necessary
$range = new TextRange($textNodes);
$ranges = array();
while(FALSE !== $start = strpos($range, $search))
{
$base = $range->split($start);
$range = $base->split(strlen($search));
$ranges[] = $base;
};
// wrap every each matching textnode
foreach($ranges as $range)
{
foreach($range->getNodes() as $node)
{
$span = $doc->createElement('span');
$span->setAttribute('class', 'search_hightlight');
$node = $node->parentNode->replaceChild($span, $node);
$span->appendChild($node);
}
}
}
If you adopt it for multiple search terms, I would add an additional class with a number depending on the search term so you can nicely style it with CSS in different colors.
Additionally you should remove duplicate search terms and make the xpath expression aware to not look for text that is already part of an element that has the highlight span assigned.
If it were me I'd have used javascript.
But using PHP, since the problem only seems to be duplicate entries in the search, just remove them, also you can run preg_replace just once rather than multiple times....
$string = 'jessie j price tag feat b o b';
$words = array('jessie','tag','b','o','b');
print hl($string, $words);
function hl($inp, $words)
{
$replace=array_flip(array_flip($words)); // remove duplicates
$pattern=array();
foreach ($replace as $k=>$fword) {
$pattern[]='/\b(' . $fword . ')\b/i';
$replace[$k]='<b>$1<b>';
}
return preg_replace($pattern, $replace, $inp);
}
I have a list of words that I'd like to add a link to, I can do this fairly easily using preg_match_all and preg_replace:
$str = "<span class=\"cz\">Dám si jedno pivo prosím.</span> = I'll have a beer please.";
preg_match_all('/[a-zťúůýžáčďéěíňóřš]+/i',$str,$matches);
$matches = array_unique($matches[0]);
foreach ($matches as $match) {
if(!empty($words[$match])) {
$str = preg_replace("/(^|[^\w]){1}(".preg_quote($match,"/").")($|[^\w]){1}/i", '\\1\\2\\3', $str);
}
}
echo $str;
What I'd like to do is restrict the linking to only within the span tag.
My brain is all regex-ed out, so any help would be appreciated! Thanks!
Darren.
preg_match_all('/[a-zťúůýžáčďéěíňóřš]+(?=\s*?</span>)/i',$str,$matches);
/( *<SPAN*>)([^<]*)(<\/SPAN>)/i
I think something like this should work, but will break if you have other tags inside your SPAN. I'd advise you to use the DOM functions instead.