The code below will do the work:
$string = "This is a very [adjective] [noun], and this is a [adjective] [noun]."
function replace_word ( $matches )
{
$replaces = array(
'[adjective]' => array("big", "small", "good", "bad"),
'[noun]' => array("house", "dog", "car")
);
return $replaces[$matches[0]][array_rand($replaces[ $matches[0] ])];
}
echo preg_replace_callback("(\[.*?\])", "replace_word", $string);
First, we regular expression match on the [something]
parts of the word, and call the replace_word()
callback function on it with preg_replace_callback()
. This function has an internal $replaces
two dimension deep array defined inside, each row defined in a [word type] => array('rep1', 'rep2', ...)
format.
The tricky and a bit obfuscated line is the return $replaces[$matches[0]][array_rand($replaces[ $matches[0] ])];
. If I chunk it down a bit, it'll be a lot more parsable for you:
$random = array_rand( $replaces[ $matches[0] ] );
$matches[0]
is the word type, this is the key in the $replaces
array we are searching for. This was found by regular expression in the original string. array_rand()
basically selects one element of the array, and returns its numerical index. So $random
right now is an integer somewhere between 0
and the (number of elements - 1)
of the array containing the replaces.
return $replaces[ $matches[0] ][$random];
This will return the $random
th element from the replace array. In the code snippet, these two lines are put together into one line.
Showing one element only once
If you want disjunct elements (no two adjective or noun repeated twice), then you will need to do another trick. We will set the $replaces
array to be defined not inside the replace_word()
function, but outside it.
$GLOBALS['replaces'] = array(
'[adjective]' => array("big", "small", "good", "bad"),
'[noun]' => array("house", "dog", "car")
);
Inside the function, we will set the local $replaces
variable to be a reference to the newly set array, with calling $replaces = &$GLOBALS['replaces'];
. (The &
operator sets it a reference, so everything we do with $replaces
(remove and add elements, for example) modifies the original array too. Without it, it would only be a copy.)
And before arriving on the return
line, we call unset()
on the currently to-be-returned key.
unset($replaces[$matches[0]][array_rand($replaces[ $matches[0] ])]);
The function put together now looks like this:
function replace_word ( $matches )
{
$replaces = &$GLOBALS['replaces'];
unset($replaces[$matches[0]][array_rand($replaces[ $matches[0] ])]);
return $replaces[$matches[0]][array_rand($replaces[ $matches[0] ])];
}
And because $replaces
is a reference to the global, the unset()
updates the original array too. The next calling of replace_word()
will not find the same replace again.
Be careful with the size of the array!
Strings containing more replace variables than the amount of replace values present will throw an Undefined index E_NOTICE
. The following string won't work:
$string = "This is a very [adjective] [noun], and this is a [adjective] [noun]. This is also an [adjective] [noun] with an [adjective] [noun].";
One of the outputs look like the following, showing that we ran out of possible replaces:
This is a very big house, and this is a big house. This is also an small with an .