It looks like a feature where you could supply one placeholder in a prepared statement, but give it an array of values, and it would expand the placeholders to fit the array. So if the query was like this:
SELECT * FROM table WHERE id IN (:idlist)
and you passed an array with 3 values for idlist, it would replace the query like this:
SELECT * FROM table WHERE id IN (:idlist_1, :idlist_2, :idlist_3) ... then use the values in the array as the three values for those placeholders. It looks like the old code was using the keys from the data array, so instead of appending someting like "_1", it would append the actual key. So an attacker could put SQL code into the array keys and it would stick those (unchanged) into the query.
Here is the old code (without comments):
foreach (array_filter($args, 'is_array') as $key => $data) {
$new_keys = array();
foreach ($data as $i => $value) {
$new_keys[$key . '_' . $i] = $value;
}
$query = preg_replace('#' . $key . '\b#', implode(', ', array_keys($new_keys)), $query);
And the new code:
foreach (array_filter($args, 'is_array') as $key => $data) {
$new_keys = array();
foreach (array_values($data) as $i => $value) {
$new_keys[$key . '_' . $i] = $value;
}
$query = preg_replace('#' . $key . '\b#', implode(', ', array_keys($new_keys)), $query);
array_values will return an array with numeric indexes, which is what removes the vulnerability.