You’ve made a theme and it has all the options you want to include. The next step is making sure that user input is the right information or at least what you are looking to use.
Why?
Security is a big thing and I mean a big thing. If you think it’s not I will kindly ask you to read something else. Who is to say that a user accidentally puts in a random letter when you are expecting a number. It’s like playing rock/paper/scissors and somebody uses magma. I mean there are only three things to choose from so how can magma be a valid option?
That is where validation comes into play when it comes to your theme options.
Validation
Say we are including a widget. That widget uses a URL for an image or even a video. How does one check for that? Or better yet, you want the user to define how many recent posts to show on the side. What would you use? Some simple examples are:
// is it a valid option $options = array( 'rock', 'paper', 'scissors' ); $is_valid = in_array( $input, $options ); // is it a number $is_number = is_int( $input );
As you can see those can seem fairly straight forward, right? What about something a little more complex like CSS, or JavaScript? That is the biggest reason that should be left to plugins. At least from a reviewer’s point of view. If you do end up using those options and not a plugin, please, please, please make sure you are validating the input. It is super critical and important you do so.
One method would be using CSSTidy and including it to a sanitize callback from the customizer. I’ve been tinkering with it for sometime and slowly dissecting how Jetpack handles their Custom CSS module. One solution I found was from a slightly outdated post on Stack Exchange. The code I’ve used is:
// this is inside the customize_register hook $wp_customize->add_setting( 'csstidy_code', array( 'type' => 'theme_mod', 'transport' => 'refresh', // This is the key part of the setting! 'sanitize_callback' => 'csstidy_sani_cb' ) ); // Outside of the customize_register hook function csstidy_sani_cb( $input ){ // load the needed file to validate/sanitize CSS require_once ( plugin_dir_path( __FILE__ ) . '/lib/class.csstidy.php' ); $csstidy = new csstidy(); $csstidy->set_cfg( 'remove_bslash', false ); $csstidy->set_cfg( 'compress_colors', false ); $csstidy->set_cfg( 'compress_font-weight', false ); $csstidy->set_cfg( 'optimise_shorthands', 0 ); $csstidy->set_cfg( 'remove_last_;', false ); $csstidy->set_cfg( 'case_properties', false ); $csstidy->set_cfg( 'discard_invalid_properties', true ); $csstidy->set_cfg( 'css_level', 'CSS3.0' ); $csstidy->set_cfg( 'preserve_css', true ); $csstidy->set_cfg( 'template', plugin_dir_path( __FILE__ ) . '/lib/wordpress-standard.tpl' ); // Sanitize of random things because we always, always trust user input, right? $css = preg_replace( '/\\\\([0-9a-fA-F]{4})/', '\\\\\\\\$1', $input ); // Some people put weird stuff in their CSS, KSES tends to be greedy $css = str_replace( '<=', '<=', $css ); // Why KSES instead of strip_tags? Who knows? $css = wp_kses_split( $css, array(), array() ); $css = str_replace( '>', '>', $css ); // kses replaces lone '>' with > // Why both KSES and strip_tags? Because we just added some '>'. $css = strip_tags( $css ); // Finally we parse the CSS $csstidy->parse( $css ); // This is the part that returns the printed CSS to save to the database $valid = $csstidy->print->plain(); return $valid; }
As you can see, I opted to do it with a plugin in mind. Part of that reason is that it will look for a theme mod rather than an option so it ties it to the currently active theme. The classes used are the csstidy, csstidy_optimize, and csstidy_print which correspond to the files and their respective filenames. The last file needed is the data.inc file because that is where all the CSS information is kept and tested against to validate the CSS.
From there you can output the CSS using the wp_head hook like so:
add_action( 'wp_head', 'csstidy_css_output' ); function csstidy_css_output(){ $css = get_theme_mod( 'csstidy_code' ); echo " <style id='csstidy-css'> {$css} </style> "; }
Yes, may seem like a crazy amount for one setting, or user option but would you rather have a safe site or zombie site?
Sanitation
Working in a restaurant can be a good thing and a bad thing. Working with food in general can be the same. Code can often relate to this. We don’t want our code to be covered with icky things do we? This is where sanitation comes into play. WordPress has several neat functions that help sanitize user input. A cool example is the sanitize_file_name() function.
The documentation says it best:
Removes special characters that are illegal in filenames on certain operating systems and special characters requiring special escaping to manipulate at the command line.
Escaping
Best way to think about it is like making sure a baby doesn’t get a hold of the kitchen knife. Okay, not literally like that but you sort of get the idea. It’s that last little measure so that a user doesn’t end up seeing weird things like pop up windows, or gets redirected to a site. What even cooler is that WordPress does have a few functions to help with that as well. What’s neater is that some of those can be used for sanitation as well. A good example is the esc_url and esc_url_raw functions.
The thing to remember is when you are using a getter function like get_the_title, or get_the_ID you may have to check and validate before you output the information. So something so simple as:
$title = get_the_title(); <h2 class="post-title"><?php echo $title; ?></h2>
Can, or may end up looking like this:
$title = get_the_title(); <h2 class="post-title"><?php echo esc_html( $title ); ?></h2>
As you can see you are creating more work in the long run. More code to maintain and who wants to do that?
Final remarks
So, please, please, take user security into consideration when it comes to your theme options. I’ve uploaded the version to github and is available to use, abuse and do what you please with it.