Techonologies Used: PHP
Introduction
PHP has a few wonderful functions in str_replace() and its regular expression cousin preg_replace(). The applications for these functions are innumerable, ranging from stripping punctuation out of a string or replacing double quotes with single quotes all the way up to converting a string from XML to JSON formatting. For all their use though, there is a huge hole in their functionality; the ability to replace a single string (or regular expression match) with a series of replacement values. Here I will present a way to have that functionality simply without the overhead of looping structures.
Examples
Example 1:
Replace the ‘a’s in “abracadabra” with all the vowels in order:
Original: abr
ac
ad
abr
a
Altered: abr
ec
id
obr
u
$result = preg_replace(array_fill(0, 5, "/a/"), array("a", "e", "i", "o", "u"), "abracadabra", 1);
Example 2:
Replace the question marks in a query with values (mimic prepared statements):
Original: SELECT * FROM users WHERE gender=
? AND age>
? AND state=
?
Altered: SELECT * FROM users WHERE gender=
‘f’ AND age>
23 AND state=
‘OR’
$result = preg_replace(array_fill(0, 3, "/\?/"), array("'f'", "23", "'OR'"), "SELECT * FROM users WHERE gender=? AND age>? AND state=?", 1);
Example 3:
Replace the asterisks in a list with numbers in order:
Original: The first amendment guarantees freedoms of
*) religion,
*) speech,
*) press,
*) petition and assembly, and
*) association.
Altered: The first amendment guarantees freedoms of
1) religion,
2) speech,
3) press,
4) petition and assembly, and
5) association.
$result = preg_replace(array_fill(0, 5, "/\*/"), range(1, 5), "The first amendment guarantees freedoms of *) religion, *) speech, *) press, *) petition and assembly, and *) association.", 1);
Prepared Statements
The goal here, replacing the occurrences of a repeated substring with a series of values in an array, is one commonly performed in the context of prepared statements. If you aren’t familiar with prepared statements, you might want to take a look at the php.net page on the topic. In short, the idea is that you build a string with wildcards either in the form of question marks (regex matched on \?) or named placeholders (regex matched on :\w+). These placeholders can then be easily replaced with the actual values later on. This replacement is handled one placeholder/value pair at a time using the functions stored in the PDO class. Replacing the placeholders one at a time can be clunky though, and if you aren’t actually using the prepared statement, including and instantiating the PDO class is a waste.
Working Around str_replace
The php.net page on str_replace() goes into great depth on how to use arrays for the arguments, in all the myriad combinations of relative their relative lengths. Unfortunately, all it has to say about the combination of use here (one search value, an array of replace values) is an insistence that “the converse would not make sense, though,” and the documentation for preg_replace() says even less. This won’t do at all. Thankfully, not all is lost, and even if the *_replace() functions don’t specifically support multiple replacement values for a single search value, that behavior can be coerced by feeding in some slightly unusual parameters.
Both functions will replace placeholder/value pairs one to one if the first two arguments are arrays of equal length. The trick to replacing only one search value (which may appear multiple times) is to provide an array filled with that one search string/regex multiple times — once for each replacement value. Of course, little is gained if such an array has to be built element by element in a forloop, but php offers a solution here in the form of the array_fill() function which allows the easy creation of an array with the same length of the replacements array, filled completely with the placeholder. This only gets halfway to the goal, however, as the *_replace() functions’ default behavior is to replace all instances of the search argument with the replacement, not the one for one replacement desired. This isn’t something one can get around with the str_replace() function, but the preg_replace() function does support an optional 4th parameter which limits the number of replacements per match. While it is true that preg_replace() is a a little slower than str_replace() for replacing plain substrings, the difference is a small enough cost for the extra functionality it provides.
The code
Putting the pieces together finally gives:
$replacements = array("value_1", "value_2", "value_3", "etc.");
$search_pattern = "/\?/";
$search_array = array_fill(0, sizeof($replacements), $search_pattern);
$result = preg_replace($search_array, $replacements, $subject, 1);
or if you prefer it all condensed down into a single (more compact but somewhat less readable) line:
$result = preg_replace(array_fill(0, sizeof($replacements = array("value_1", "value_2", "value_3", "etc.")), "/\?/"), $replacements, $subject, 1);
or as a function takes the same arguments and works the same as
preg_replace(), but runs the code above in the case of a single search pattern and an array of replacements:
function preg_replace_plus($search_pattern, $replacement, $subject, $limit=1, &$count=null) {
if (is_array($replacement) && !is_array($search_pattern)) {
$result = preg_replace(array_fill(0, sizeof($replacement), $search_pattern), $replacement, $subject, 1, $count);
}
else {
$result = preg_replace($search_pattern, $replacements, $subject, $limit, $count);
}
return $result;
}
The search string in the above can of course be replaced with whatever your placeholder might be, though since this only works through the preg_replace() function, literal search strings still need to be expressed as regular expressions (escaping the regex meta-characters and adding the necessary delimiters).