Escaping lists of strings for SQL in WordPress

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 esc_sql and 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!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.