Today I found myself wanting to use a query similar to
SELECT DISTINCT post_id FROM $wpdb->posts WHERE meta_key IN ( 'string_x', 'string_y', 'string_z' ), but where I needed to construct the values for the list of strings from an array of untrusted values (because we never trust inputs, we always look suspiciously on them if they’re going near the DB). Looking through the core code, it seems like there’s currently one place it does this and, while less elegant than the WPDB class
prepare method it’s worth me remembering for the future, so…
The technique is to run the elements of the array through the
sanitize_title_for_query functions which WordPress provides;
esc_sql essentially throws things over to a method on the WPDB object to add slashes, and
sanitize_title_for_query which by default (it’s extendable through filters) runs it through
sanitize_title_with_dashes to replace whitespace and a few other characters with dashes, limits the output to alphanumeric characters, underscore (_) and dash (-) converting whitespace to a dash.
Here’s the code I’m using:
// $meta_keys is an array of strings equating
// to meta_keys in the postmeta table
$meta_keys = array_map( 'esc_sql', $meta_keys );
$meta_keys = array_map( 'sanitize_title_for_query', $meta_keys );
$meta_keys_str = "'". implode( "','", $meta_keys ) . "'";
$post_ids = $wpdb->get_col( "SELECT DISTINCT post_id FROM $wpdb->postmeta WHERE meta_key IN ( $meta_keys_str )" );
Let me know if I’m doing_it_wrong, but otherwise I hope this helps someone else, probably that someone will be me in the future!