StripWords with Multiple Replacements

Use "find and replace" pairs to modify resource content


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 ccould 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 are exactly the same length.

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. Here's the conventional format for the wordsToReplace chunk:

hell:heck,damn:darn,MODx:MODX

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('wordsToStrip');
$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 a sanity 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];
}
[code]

<p>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 replace with nothing, which you might want. For example, you might want to remove some entries and replace others bu using a string like this in the chunk:</p>

<pre class="brush: html; toolbar: false;"><fixedpre>
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('wordsToStrip');
$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 if 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 effect 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.



Comments (0)


Please login to comment.

  (Login)