Go Back   CodingForums.com > :: Server side development > PHP

Before you post, read our: Rules & Posting Guidelines

Reply
 
Thread Tools Rate Thread
Enjoy an ad free experience by logging in. Not a member yet? Register.
Old 12-07-2012, 03:37 PM   PM User | #1
shaunthomson
New Coder

 
Join Date: May 2012
Posts: 89
Thanks: 51
Thanked 0 Times in 0 Posts
shaunthomson is an unknown quantity at this point
Can someone see the error in my logic?

Gidday

I'm making a basic keyword highlighter:

Code:
        $string = "Here be my string la";

         //trim up keywords, and put them into $words array ...
	$term = preg_replace('/(\s+)/i', ' ', trim("la"));		
	$words = explode(' ', $term);
	
	$highlighted = array();
	
	foreach ( $words as $word ){
		$highlighted[] = "<span class='H'>".ucwords(htmlentities($word, ENT_NOQUOTES, 'UTF-8'))."</span>";			
	}
	
	//replace any hits with the highlighted version...

	return  str_ireplace($words, $highlighted, $string);
It works fine, except if your keyword resembles anything in span class='H' eg 'la'

I understand that it's finding 'la' within the span, and then putting a span around it. What I don't understand is HOW? I figure I'm misunderstanding str_ireplace, but doesn't it say 'if $words[0] is in "string", replace it with $highlighted[0]', and so on through the arrays. How is it finding 'la' in
Code:
<span class='H'>".ucwords(htmlentities($word, ENT_NOQUOTES, 'UTF-8'))."</span>
?

Thanks guys

Last edited by shaunthomson; 12-07-2012 at 03:48 PM..
shaunthomson is offline   Reply With Quote
Old 12-07-2012, 04:36 PM   PM User | #2
Fou-Lu
God Emperor


 
Fou-Lu's Avatar
 
Join Date: Sep 2002
Location: Saskatoon, Saskatchewan
Posts: 15,662
Thanks: 4
Thanked 2,452 Times in 2,421 Posts
Fou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to all
str[i]_replace are greedy in regards to an array. So it will take the string in its entirety, and check for replacements on EACH iteration of item provided by the array needle.
So if I do like so:
PHP Code:
$aWords = array('cat''dog''mouse''la');
$aHighlight = array();
$str 'The dog chases the cat which chases the mouse';

foreach (
$aWords AS $word)
{
    
$aHighlight[] = sprintf('<span class="H">%s</span>'ucwords($word));
}

print 
str_ireplace($aWords$aHighlight$str); 
The results would be in sequence:
Code:
The dog chases the cat which chases the mouse <-- evaluate 'cat'
The dog chases the <span class="H">Cat</span> which chases the mouse <-- evaluate 'dog'
The <span class="H">Dog</span> chases the <span class="H">Cat</span> which chases the mouse <-- evaluate 'mouse'
The <span class="H">Dog</span> chases the <span class="H">Cat</span> which chases the <span class="H">Mouse</span> <-- evaluate 'la'
The <span c<span class="H">La</span>ss="H">Dog</span> chases the <span c<span class="H">La</span>ss="H">Cat</span> which chases the <span class="H">La</span> <span c<span class="H">La</span>ss="H">Mouse</span> <-- end result
Because of the purpose of the above, there isn't really a great way to do this with an string match/replace technique (you'll need to keep backtracking and checking new terms).

Instead, simply use pattern matching.
PHP Code:
$str 'The dog chases the cat which chases the mouse';

$aWords = array('cat''dog''mouse''la');
array_walk($aWordscreate_function('&$s''$s = preg_quote($s, "/");'));
$sPattern '/(' implode('|'$aWords) . ')/i';

print 
preg_replace_callback($sPattern,
    
create_function(
        
'$s'
        
'return sprintf(\'<span class="H">%s</span>\', ucwords($s[0]));'
        
),
    
$str
    
); 
Try that.
Fou-Lu is offline   Reply With Quote
Users who have thanked Fou-Lu for this post:
shaunthomson (12-07-2012)
Old 12-07-2012, 05:04 PM   PM User | #3
shaunthomson
New Coder

 
Join Date: May 2012
Posts: 89
Thanks: 51
Thanked 0 Times in 0 Posts
shaunthomson is an unknown quantity at this point
Thanks Fou - I thought I was going nuts. Your explanation and alternative are much appreciated.
shaunthomson is offline   Reply With Quote
Old 12-07-2012, 05:36 PM   PM User | #4
Fou-Lu
God Emperor


 
Fou-Lu's Avatar
 
Join Date: Sep 2002
Location: Saskatoon, Saskatchewan
Posts: 15,662
Thanks: 4
Thanked 2,452 Times in 2,421 Posts
Fou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to all
Yep you bet. Also, you can check the API here; I didn't realize that they did actually indicate that this would happen (check the caution note just before the user comments): http://ca2.php.net/manual/en/function.str-replace.php
Fou-Lu is offline   Reply With Quote
Old 12-07-2012, 11:14 PM   PM User | #5
AndrewGSW
Senior Coder

 
Join Date: Apr 2011
Location: London, England
Posts: 2,120
Thanks: 15
Thanked 354 Times in 353 Posts
AndrewGSW will become famous soon enough
I did this in a slightly different way and don't know if the following code is of any interest - apologies if not.

PHP Code:
function onlyWholeWords(&$value$key) {
    
// Ignores words after // comment delimiters.
    //$value = "/\b(" . $value . ")\b/";    // doesn't handle comments, so..
    //$value = "/^(?:(?!\/\/).)*\K\b(" . $value . ")\b/"; 
    // \K lookbehind alternative is not supported in PHP < 5.2.4, so use:
    
$value "/^((?:(?!\/\/).)*)\b" $value "\b/";
}
function 
addSpan(&$value$key$color='blue') {
    
$value "$1<span style='color:$color'>" $value "</span>";
}
function 
codeWords($code) {
    
$keywords = array('as''break''case''class''continue''default'
        
'do''elif''else''elseif''for''foreach''function''if'
        
'new''null''return''self''switch''this''to''typeof'
        
'until''var''void''while''with');
    
$keywords2 $keywords;

    
array_walk($keywords'onlyWholeWords');
    
array_walk($keywords2'addSpan''blue');
    
$code preg_replace($keywords$keywords2$code);
    return 
$code;

It might be of interest because it uses functions and it can be modified to use different colours for different (arrays of) words:

PHP Code:
function codeWords($code$colour)
// ...
    
array_walk($keywords2'addSpan'$colour); 
__________________
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
Validate your HTML and CSS
AndrewGSW is offline   Reply With Quote
Old 12-08-2012, 02:33 PM   PM User | #6
rgb
New Coder

 
Join Date: Jul 2011
Posts: 12
Thanks: 0
Thanked 2 Times in 2 Posts
rgb is an unknown quantity at this point
As a newcomer I'm learning a lot from this forum and I've been playing with some of the suggestions on this topic.
Wouldn't something simple like this do the job? Playing with Fou-Lou's latter example I notice that substituting 'concatenation' for 'cat' in $str echos out as "conCatenation". This simple version fixes that and also allows for punctuation to be appended to a keyword.

Code:
echo "<style>.H{color:red;}</style>";
 	$str = 'The dog chases the cat, which chases the mouse.';
	$aWords = array('cat', 'dog', 'mouse', 'la');

	$str = explode(' ',$str);
	foreach($str as $word){
		$remove = array(',', ';', '.'); // ignore common bits of puntuation
		$trimmedword = strtolower(str_replace($remove, '', $word));// in case key words are upper case or capitalised
		if(in_array($trimmedword,$aWords)){
			echo "<span class=H>".ucwords($word)."</span>";
			}
			else {
				echo $word;
			}
			echo "&nbsp;";
		}
Is this too simple? Am I missing something?

Last edited by rgb; 12-08-2012 at 03:10 PM..
rgb is offline   Reply With Quote
Old 12-08-2012, 04:23 PM   PM User | #7
Fou-Lu
God Emperor


 
Fou-Lu's Avatar
 
Join Date: Sep 2002
Location: Saskatoon, Saskatchewan
Posts: 15,662
Thanks: 4
Thanked 2,452 Times in 2,421 Posts
Fou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to all
Quote:
Originally Posted by rgb View Post
As a newcomer I'm learning a lot from this forum and I've been playing with some of the suggestions on this topic.
Wouldn't something simple like this do the job? Playing with Fou-Lou's latter example I notice that substituting 'concatenation' for 'cat' in $str echos out as "conCatenation". This simple version fixes that and also allows for punctuation to be appended to a keyword.

Code:
echo "<style>.H{color:red;}</style>";
 	$str = 'The dog chases the cat, which chases the mouse.';
	$aWords = array('cat', 'dog', 'mouse', 'la');

	$str = explode(' ',$str);
	foreach($str as $word){
		$remove = array(',', ';', '.'); // ignore common bits of puntuation
		$trimmedword = strtolower(str_replace($remove, '', $word));// in case key words are upper case or capitalised
		if(in_array($trimmedword,$aWords)){
			echo "<span class=H>".ucwords($word)."</span>";
			}
			else {
				echo $word;
			}
			echo "&nbsp;";
		}
Is this too simple? Am I missing something?
Your right I missed the word boundaries. The pattern can be simply replaced using: $sPattern = '/\b(' . implode('|', $aWords) . ')\b/i';. That will work otherwise as it echos instead of replaces. It will unfortunately replace a space with an &nbsp;, but that's easy enough to get around as well. Since it iterates each term, it will be slow.


Quote:
Originally Posted by AndrewGSW View Post
I did this in a slightly different way and don't know if the following code is of any interest - apologies if not.

PHP Code:
function onlyWholeWords(&$value$key) {
    
// Ignores words after // comment delimiters.
    //$value = "/\b(" . $value . ")\b/";    // doesn't handle comments, so..
    //$value = "/^(?:(?!\/\/).)*\K\b(" . $value . ")\b/"; 
    // \K lookbehind alternative is not supported in PHP < 5.2.4, so use:
    
$value "/^((?:(?!\/\/).)*)\b" $value "\b/";
}
function 
addSpan(&$value$key$color='blue') {
    
$value "$1<span style='color:$color'>" $value "</span>";
}
function 
codeWords($code) {
    
$keywords = array('as''break''case''class''continue''default'
        
'do''elif''else''elseif''for''foreach''function''if'
        
'new''null''return''self''switch''this''to''typeof'
        
'until''var''void''while''with');
    
$keywords2 $keywords;

    
array_walk($keywords'onlyWholeWords');
    
array_walk($keywords2'addSpan''blue');
    
$code preg_replace($keywords$keywords2$code);
    return 
$code;

It might be of interest because it uses functions and it can be modified to use different colours for different (arrays of) words:

PHP Code:
function codeWords($code$colour)
// ...
    
array_walk($keywords2'addSpan'$colour); 
That's neat, but it still won't work. The problem is that if you use str_replace it will become greedy on each iteration. If you use an array of pattern and replace then they will also be greedy since each evaluation is distinct on the subject. So it would ultimately give the same behaviour as an str_replace. If you change the span style to a span class, then add 'cat' (for concatination ), then you would end up with it replacing both the span to the cat with the class, as well as the class when it hits it further in. It would be useful for C syntax highlight, but not useful for a search highlight (albeit redundant since PHP has a builtin highlight function).

Last edited by Fou-Lu; 12-08-2012 at 04:26 PM..
Fou-Lu is offline   Reply With Quote
Old 12-08-2012, 04:43 PM   PM User | #8
AndrewGSW
Senior Coder

 
Join Date: Apr 2011
Location: London, England
Posts: 2,120
Thanks: 15
Thanked 354 Times in 353 Posts
AndrewGSW will become famous soon enough
@Fou-Lu Thank you. Actually, I recall now that I'm cheating

PHP Code:
                    $line codeWords($line);       // blue keywords
                    
$line str_replace('  ''&nbsp;'$line);
                    
// Use @ to break-up keywords that shouldn't be colour-coded;
                    // use @@ to keep a single @ sign.
                    
$line preg_replace('/((\@)(\@)?)/''$3'$line);
                    
$html_lines .= "<li class=\"tab$count_fours\">$line</li>"
I'm using at @ signs to break words that I wish to exclude. So my example is not directly relevant to the OPs question. Sorry for the confusion .
__________________
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
Validate your HTML and CSS
AndrewGSW is offline   Reply With Quote
Old 12-08-2012, 04:48 PM   PM User | #9
AndrewGSW
Senior Coder

 
Join Date: Apr 2011
Location: London, England
Posts: 2,120
Thanks: 15
Thanked 354 Times in 353 Posts
AndrewGSW will become famous soon enough
Quote:
PHP has a builtin highlight function
Mmm, might investigate this, but then again.. I've already done it
__________________
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
Validate your HTML and CSS
AndrewGSW is offline   Reply With Quote
Old 12-08-2012, 04:53 PM   PM User | #10
Fou-Lu
God Emperor


 
Fou-Lu's Avatar
 
Join Date: Sep 2002
Location: Saskatoon, Saskatchewan
Posts: 15,662
Thanks: 4
Thanked 2,452 Times in 2,421 Posts
Fou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to allFou-Lu is a name known to all
Quote:
Originally Posted by AndrewGSW View Post
Mmm, might investigate this, but then again.. I've already done it
You betcha:
PHP Code:
$sCode = <<<CODE
<?php
echo "This is some code";
$aVar = array('var');
printf('This is a variable: %s' PHP_EOL$aVar[0]); // And a comment
?>
CODE;

print highlight_string($sCode);
There is also a highlight_file method. This will be faster than any user method that can be written of course.
Fou-Lu is offline   Reply With Quote
Old 12-08-2012, 05:05 PM   PM User | #11
AndrewGSW
Senior Coder

 
Join Date: Apr 2011
Location: London, England
Posts: 2,120
Thanks: 15
Thanked 354 Times in 353 Posts
AndrewGSW will become famous soon enough
Mmm got that page open (highlight_string) but it's just for PHP code..

My approach is for displaying database-content as code in a kindof-blog.
Attached Thumbnails
Click image for larger version

Name:	blogcode1.png
Views:	11
Size:	21.7 KB
ID:	11774  
__________________
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
Validate your HTML and CSS

Last edited by AndrewGSW; 12-08-2012 at 05:14 PM..
AndrewGSW is offline   Reply With Quote
Reply

Bookmarks

Jump To Top of Thread


Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 07:00 AM.


Advertisement
Log in to turn off these ads.