Reputation: 2732
I need to search through some content and enclose certain words with HTML. The words to search for are in an array, and I want to only replace the 2nd to last occurrence of each set of matches. So, as an example, if there are 5 matches, I want to replace the 4th match. If there are 3 matches, replace the 2nd. If there are 2 matches, replace the 2nd/last match, and if only one match then use that one.
The callback function is external to the function using the callback.
I'm thinking I need an array count -- minus one -- and then pass that number to the callback function and use it there, but maybe there's an easier way to do this?
Simplified functions are:
function repWords($content) {
foreach ($words_array as $w) {
$content = preg_replace_callback('/\b('.$w.'[s]?)\b/S', array(&$this,'addHtml'), $content);
}
return $content;
}
function addHtml($format){
return '<span>' . $format[1] . '</span>'
}
This works as desired to replaced all words it finds, and I can set it up to randomly replace some, but I want to either replace the nth occurrence and/or replace only one occurrence (not always the first occurrence).
Upvotes: 0
Views: 633
Reputation: 32532
Based on your requirements, here is what I came up with:
// needs to be global given that you are using non-anonymous function syntax
$count = 0;
function repWords($content) {
global $count;
$words_array = array('some','content','foobar');
foreach ($words_array as $w) {
$pattern = '~\b('.$w.'s?)\b~';
$count=0;
preg_match_all($pattern,$content,$matches);
$count=count($matches[0]);
if ($count>0)
$content = preg_replace_callback($pattern,'addHtml',$content);
}
return $content;
}
function addHtml ($format) {
global $count;
static $cc=1;
static $pf='';
$val=$format[1];
$cc = ($pf!=$val) ? 1 : ++$cc;
if ((1==$count)||($cc==($count-1)))
$format[1]= '<span>' . $val . '</span>';
$pf=$val;
return $format[1];
}
$content = <<<EOC
this is some content
some more content
this content foobar
EOC;
echo repWords($content);
output
this is <span>some</span> content
some more <span>content</span>
this content <span>foobar</span>
Note: this utilizes a global variable $count
. In general, it's a bad idea to use global variables. But this is one of the few examples where it's grudgingly accepted, due to limitation of php and preg_replace_callback
(or any function that you can specify a callback) when you opt to define the callback separately instead of use an anonymous function. If you are on php5.3+ and are willing to make addHtml
an anonymous function instead, $count
can be passed to the anonymous function with use
. Alternatively, if all this is actually in a class, make it a class property and use $this->count
instead.
Another note: The very last thing you said was "(not always the first occurrence)" I was somewhat confused by this and took it to possibly mean that you want to somehow be able to specify the nth* instead of it always being "2nd to last (or first if there's only 1" e.g. in my code example perhaps you want to change it to be every 3rd to last word instead of 2nd to last. I asked you to clarify but you didn't respond by the time I posted this solution, so I did not write the code to be flexible for that. However, hopefully you should be able to take this and alter it to suit you if that's what you want. Basically it will involve passing another arg to repWords
and it will also involve using another global variable to use in addHtml
, where $cc==($count-1)
is used.
Upvotes: 1