In the previous article, we looked at a custom output modifier (and plugin) to remove or replace unwanted words or phrases from a web page. The output modifier could be used with any tag in MODX revolution.
In this article we'll see how to allow a different replacement string for each problem word or phrase. We'll also take a look at techniques for parsing strings.
Multiple Replacements
Suppose that you want to do multiple replacements to text on your site. The PHP str_replace()
function will take an array for both its first and second argument (lets call the two arrays $find
and $replace
). The function replaces the first value in the $find
array with the first value in the $replace
array. Then it replaces the second value in the $find
array with the second value in the $replace
array — and so on through the arrays. It's important that the two arrays have the same number of elements.
In the previous example, we used a chunk to hold the strings. We'll do that here too.
We could put two separate lines in the chunk, one for the 'find' array and one for the 'replace' array. It's more convenient, though to create a single line with pairs of find/replace entries. It's also a little easier to parse them that way. Since we're not actually stripping the words, let's call the chunk WordsToReplace Here's the conventional format for the WordsToReplace chunk:
hell:heck,damn:darn,MODx:MODX
In the chunk, the pairs of words are delimited by a comma. The two words of each pair are separated by a colon.
In our snippet, we need to process that string to create our two arrays. The first step is to initialize our two arrays, get the chunk's content, and split it at the commas to get each pair (find/replace):
$find = array();
$replace = array();
$line = $modx->getChunk('WordsToReplace');
$pairs = array_map('trim', explode(',', $line));
Using array_map
and trim
makes sure that there are no leading or trailing spaces, tabs, or linefeeds, in case the user has mis-entered the values. At this point, the $pairs
array looks like this:
array(
[0] => 'hell:heck'
[1] => 'damn:darn'
[2] => 'MODx:MODX'
)
Next, we need to walk through that array, adding the appropriate values to our two arrays. We'll add an error check to make sure each member of the $pairs
array has a colon:
foreach($pairs as $pair) {
if (strpos($pair, ':') === false) {
// $modx->log(modX::LOG_LEVEL_ERROR, '[stripWords] Parse error - missing colon');
/* add a colon to prevent null entries */
$pair = $pair . ':';
}
$couple = array_map('trim', explode(':', $pair));
$find[] = $couple[0];
$replace[] = $couple[1];
}
The modx log line is optional. It will report missing colons, but you may not want to treat that as an error. For entries with a missing colon, the word will be replaced with nothing, which you might want. For example, you might want to remove some entries and replace others by using a string like this in the chunk:
hell,damn,MODx:MODX
With that string, the first two would be removed and 'MODx' would be replaced with 'MODX'.
Finally, we need to use a slight modification to our output modifier to use the two arrays:
return str_replace($find, $replace , $input);
The Full Code
Putting it all together, here's the full code of our stripWords output modifier:
$find = array();
$replace = array();
$line = $modx->getChunk('WordsToReplace');
$pairs = array_map('trim', explode(',', $line));
foreach($pairs as $pair) {
if (strpos($pair, ':') === false) {
// $modx->log(modX::LOG_LEVEL_ERROR, '[stripWords] Parse error - missing colon');
/* add a colon to prevent null entries */
$pair = $pair . ':';
}
$couple = array_map('trim', explode(':', $pair));
$find[] = $couple[0];
$replace[] = $couple[1];
}
return str_replace($find, $replace , $input);
Speed Considerations
If page-load speed are critical for you, especially if the cache is often cleared, you can speed things up very slightly by doing the replacements in a plugin attached to the OnWebPagePrerender
System Event. The only change to the code would be using $modx->resource->_output
for the content instead of $input
, so the last line of the code would be replaced by this:
$modx->resource->_output = str_replace($find, $replace , $modx->resource->_output);
return '';
The speed improvement would be very insignificant, however. It would only affect pages that are not cached and would only cut out the tiny amount of time necessary for MODX to identify the output modifier. Since it's not a conditional modifier and there are no arguments to parse, that would probably take just a few milliseconds.
Another possible way to speed things up would be to use strpos()
on each word to be replaced and return without calling str_replace()
if none of them are present in the content. The effects of this would also be minimal, and depending on the number of words involved, it might actually slow things down. You'd have to benchmark it to be sure.
Coming Up
Sometimes, creating two separate arrays for str_replace()
is inconvenient and can lead to errors where the strings to be replaced don't line up with the replacements. It works very well for this use case, but in the next article, we'll look at a way to do replacements with a single associative array of keys and values.
Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.