How to do a review (Draft)

Alert: DRAFT: This page is still a draft and being actively written.
HELP WANTED: If you want to help improve this page, contact @poena via Slack. Last update: December 2 2017

Warning: This page is opinionated. It should not be used in place of the requirements page.  

What we expect of themes submitted to WordPress.org What we expect of themes submitted to WordPress.org

The theme should:

  • be GPL compatible.
  • be secure.
  • be free of PHP or JS notices.
  • not be in conflict with plugins e.g. prefixing.
  • be translation ready.
  • use WordPress functions, hooks, filters and libraries.
  • not do anything illegal, dishonest, or morally offensive.

Top ↑

Preparations Preparations

This assumes that you have a testing environment set up.  If you haven’t already, please read our related handbook pages: Become a reviewer, Theme review process and Working with Trac.

Note that you need to be able to switch between PHP 7 and 5.

There are several plugins that can help you with the review, but you will also need to check all the files manually (yes that is correct: all the files).

You will need access to the requirements page, the developer code reference and the theme developer handbook.

Tip: If you need help with the review, you can ask other reviewers in the #themereview Slack channel.

Top ↑

Performing the review Performing the review

We want to encourage you to find a flow that works fastest for you. You might find it easiest to work through the list of requirements, or you might find it easier to look at file by file. Since most themes follow a standard, so can your review.

Please remember that we do not review design, but we review usability. We only require design changes if something is broken or unusable. You may add design recommendations to the review, but it is optional.

The focus of the review should be security and license. You may need to test the theme settings but you should only need to spend a couple of minutes on each.

When writing the review, separate requirements from recommendations. This makes it easier for the author to make required changes, and for other reviewers to do follow up reviews.

The most common files to find errors in are:

  • style.css Missing license information or using the wrong links.
  • header.php Hard coding scripts, styles and charset. Text missing translation functions.
  • footer.php Hard coding scripts. Options such as copyright texts that are not safely escaped on output.
  • functions.php Functions that are missing prefixes, functions that do things that we consider plugin territory.
  • customizer.php Settings that are missing sanitizing. Text missing translation functions.

We recommend starting with these files, then running the plugins and finally activating the theme and viewing the different pages and settings.

How to use the results of the Theme Check and Theme Sniffer scans:

  • Errors needs to be fixed before a theme can be approved.
  • Warnings, info, and notices are indications for something that needs to be manually checked.
  • Recommended Recommendations are not required to be fixed before a theme can be approved.

You may include the error report in your review.

By searching for specific phrases you can drastically reduce how long a review takes. There are tools available that can search zip files, such as grep. Editors like PHPStorm, Atom and Sublime Text also lets you search the entire theme folder.

Look for these boxes throughout this page for tips:

Tutorial: Search for…

Tip: tip…

Top ↑

1. License 1. License

Themes must be compatible with the GNU General Public License v2, or any later version, to be hosted on WordPress.org.

Warning: This is a blocker. If the theme is not compatible, you can stop the review and let the author know that you can’t continue the review until the licensing issues are resolved.

Here you will find more information about GPL, including a list of compatible licenses. The Theme Developer Handbook also has a chapter on Licensing.

Themes are required to include the license in the header of the style.css file.
Open style.css and make sure that these lines are in the header and that they are not blank.
Examples:

License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html

or:

License: GNU General Public License v2 or later
License URI: LICENSE

Where license.txt is included and contains a copy of the license.

The remaining license and copyright information should be included in the readme file.

Make sure that the author has included a copyright notice for the theme.
Example:

Twenty Seventeen WordPress Theme, Copyright 2016 WordPress.org

Themes need to include license and copyright information for all assets including stylesheets, scripts, fonts and images (even images used in the screenshot and images that the author has created).

Where possible, a link to the source should be included
Example:

normalize.css, Copyright 2012-2016 Nicolas Gallagher and Jonathan Neal
License: MIT Source: https://necolas.github.io/normalize.css/

If a theme incorporates code from other themes or plugins, these must also be attributed
Example:

Theme name is based on Underscores http://underscores.me/, 
(C) 2012-2017 Automattic, Inc. 
License: GNU General Public License v2 or later

You can also validate a themes readme file with this tool.

Top ↑

2. Security 2. Security

The theme needs to be as secure as possible. To be able to review this, you need a basic understanding of escaping, validating and sanitizing.

Please read and refer to the Theme Security chapter in the Theme Developer Handbook and the WordPress.com VIP security overview.

Note: All untrusted data should be escaped before output. Untrusted data includes user input, for example theme options.


A common mistake is echoing get_theme_mod(), get_option() or get_post_meta() without escaping.

A rough rule of thumb is that

  • When a WordPress function begins with “get_”, such functions generally need to be escaped.
  • When WordPress functions begin with “the_”, generally these are already escaped prior to output.

Examples
get_permalink() needs to be escaped with esc_url(): esc_url( get_permalink() )
While the link in the_permalink() is already escaped.

get_the_title() is not normally escaped since we want to allow html in titles.
To use a post title in a title attribute, the_title_attribute() should be used instead. This content is escaped.

In this example, the link to the image in header_image() is already escaped with esc_url(), but the width and height attributes need to be escaped.

<img src="<?php header_image();?>" 
width="<?php echo esc_attr( get_custom_header()->width );>"
height="<?php echo esc_attr( get_custom_header()->height );>" />

get_the_category_list() is an example of a commonly used function that starts with get_ that does not need to be escaped. The links to the categories in the function are already escaped with esc_url().

To determine if a WordPress function needs to be escaped, you can look it up in the developer reference.

You can also double check content inside html attributes. A common mistake in themes is forgetting to escape the placeholders for the comment- and search forms.

Example:
placeholder="Search..." would need to be both escaped and translation ready:
placeholder="<?php esc_attr_e( 'Search...', 'textdomain' ); ?>"

Tutorial: Search for:
echo get_, echo $
href=, source=, placeholder=, value=, alt=, title=, name=

See a list of all html attributes

Note: Validate and/or sanitize untrusted data before entering into the database.


There are basically 3 places where we allow user input to be saved: the Customizer, in meta fields, and in custom widgets. Separate option pages are no longer allowed.

The customizer The customizer

Look for the customizer file(s) and make sure that all options are sanitized and/or validated using the correct functions and methods. All settings in the customizer needs a sanitize_callback or sanitize_js_callback.

Common mistakes include:

Another problem is when a custom function is added as a sanitize_callback, but the value is returned without being sanitized.

Tutorial: Search for:

  • $wp_customize->add_setting
  • sanitize_

Customizer settings with checkboxes, radio buttons, multiple options (select and choice) and settings that require a specific format (for example numbers only) should be validated before saving.

Tip: The Theme Review Team has a managed GitHub repo which provides useful examples of various customizer features, including sanitization. This repo can be found here.

Top ↑

Custom meta boxes Custom meta boxes

Theme authors are allowed to add custom fields for design related options to posts and pages. Non design related options are not allowed, nor is adding fields to other screens than posts and pages.

Tutorial: Search for:
add_meta_box

Submitting and saving the user input

  • Make sure that a nonce is used. wp_verify_nonce should be used rather than check_admin_referer(). See Using Nonces to learn more about nonces.
  • Make sure that a capability check is used; look for current_user_can() in combination with edit_pages or edit_posts respectively. Capabilities should be used rather than roles.
  • The data needs to be sanitized and or validated with the correct functions or methods before saving.
  • Post meta-data needs to be escaped on output, whether it is displayed in the backend or frontend.

The Plugin Developer Handbook has a chapter on managing Metadata and adding custom meta boxes.

Top ↑

Custom widgets Custom widgets

Theme authors are allowed to add custom widgets that uses existing content, but also widgets that create non-trivial content.

Tutorial: Search for: WP_Widget

A custom widget can be very varied, but can have the same security issues as metaboxes.
The data needs to be sanitized and or validated with the correct functions or methods before saving. Widget data needs to be escaped on output, whether it is displayed in the back end or front end.

Tip: Even options that are only available if a plugin is installed needs to be secure.

Recommended reading
WordPress.com VIP Best practices: validating, sanitizing, escaping/
WordPress.com VIP Code Review: validating, sanitizing, escaping

Codex: Escaping: Securing Output

A guide to writing secure themes
Part 1 -introduction/
Part 2 -validation/
Part 3 -sanitization/
Part 4 -securing-post-meta/

Top ↑

3. Code 3. Code

Check the theme to make sure that there are no errors, warnings or notices.

  • After activating the theme, check for PHP notices, errors and warnings as well as JavaScript issues and missing files.
  • View archives, single post and pages. Perform a search and test the 404 page.
  • Add custom widgets, test them in both the admin and on the front end.
  • Test custom page templates.

You do not need to validate HTML or CSS.

Top ↑

PHP version compatibility PHP version compatibility

Themes are required to support PHP7. This means there must be no PHP errors or notices when running on PHP7.
Themes are allowed to use newer PHP features that are not included in PHP 5.2 or PHP 5.5, but there must not be any PHP errors when the theme is installed on a site using a lower than supported PHP version, e.g. PHP 5.x.

The Theme Sniffer plugin allows you to select a minimum PHP version, and will print an error if a theme uses features not available in that version. In those cases, you should test the theme on both PHP versions.

Theme authors can choose whether or not to allow the theme to be activated on sites using a lower PHP version.

Top ↑

Admin pointers and private functions Admin pointers and private functions

Themes should not use features/APIs meant for WP Core use only. This is not very common, so you don’t need to memorize these functions; if you look them up in the developer reference, you will see that there is a notice at the top of the page, explaining that is is not intended to be used in themes or plugins.
List of admin pointers
List of private functions.

Tutorial:
Search for:
pointer

Themes may not modify or remove non-presentational hooks.

Tutorial:
Search for:
remove_action(
Specifically:
remove_action( 'wp_head', 'wp_generator' );
remove_action( 'wp_head', 'feed_links_extra', 3);
remove_action( 'wp_head', 'feed_links', 2 );
remove_action( 'wp_head', 'rsd_link' );
remove_action( 'wp_head', 'index_rel_link' );
remove_action( 'wp_head', 'wlwmanifest_link' );
remove_action( 'wp_head', 'start_post_rel_link', 10, 0 );
remove_action( 'wp_head', 'parent_post_rel_link', 10, 0 );
remove_action( 'wp_head', 'adjacent_posts_rel_link', 10, 0 );
remove_action( 'admin_notices', 'update_nag', 3 );
remove_action( 'network_admin_notices', 'update_nag', 3 );

remove_filter( 'the_content','wpautop' );

Top ↑

Required files Required files

Parent themes are required to include:

Please see the child theme section for information about reviewing child themes.

Top ↑

File specific code requirements File specific code requirements

This can be detected using Theme Check.

Open header.php.
Make sure that the theme has a valid DOCTYPE declaration and that it includes language_attributes.
Examples:
<!DOCTYPE html>
<html <?php language_attributes(); ?>>

Using conditional comments is also allowed:
<!DOCTYPE html>
<!--[if IE 7]>
<html class="ie ie7" <?php language_attributes(); ?>>
<![endif]-->

Make sure that wp_head() is included immediately before the closing head tag.

Open footer.php.
Make sure that wp_footer() is included immediately before the closing body tag.

Top ↑

Backwards compatibility Backwards compatibility

Themes may be backwards compatible, but only for 3 major WordPress versions (version 4.5 if 4.8 is latest).

  • Themes do not need to wrap older WordPress functions in function_exists.
  • Themes should not provide fallbacks for WordPress functions added more than 3 versions ago, since we want to encourage users to upgrade their WordPress installation. This is a fairly common problem when theme authors has used an older version of underscores as a base for their theme.

Tutorial:
Search for:
function_exists

Top ↑

Deprecated functions Deprecated functions

The theme should not use any deprecated functions. When WP_DEBUG is enabled in wp-config.php, it will turn on the notices that deprecated functions were used. List of deprecated functions.

Recommended tools:
Query Monitor.
Your browser’s developer tools (Console) can help you find JS errors.

Top ↑

4. Prefix 4. Prefix

Make sure that a unique prefix is used for everything the theme defines in the public namespace, including options, functions, global variables, constants, image sizes etc.

Prefixes prevent other themes and plugins from overwriting your variables and accidentally calling your functions and classes. It will also prevent the theme from doing the same.

The prefix should be at least 3 characters long, and the recommended prefix is the theme slug.
When enqueuing files, It is recommended that authors do not prefix third party scripts and style handles unless they have made changes to the files.

Instead of:
add_image_size( 'a-small-thumbnail', 100, 100, true );
We should use:
add_image_size( 'theme-slug-small-thumbnail', 100, 100, true );
instead of
function is_woocommerce_active()
We should use a prefixed function:
function theme_slug_is_woocommerce_active()

Tutorial: Search for:

  • function (remember the space afterwards)
  • $global
  • class(remember the space afterwards)

Tip: As an author, remember to always update the prefix if you use code from the WordPress Developer Reference or Codex, or other guides and tutorials!

Recommended reading
Plugin developer handbook: prefix everything
In WordPress, prefix everything
Prefix all the things

Top ↑

5. Core Functionality and Features 5. Core Functionality and Features

Themes should use WordPress functionality and features first if available and not duplicate existing features.

When there is more than one way to achieve something, we ask that authors use the functions, filters, hooks and methods that are created specifically for this purpose.

Common problems that we need to look out for includes:

  • Custom excerpt lengths without using the correct filters.
  • Custom background or header image, video or logo functionality.
  • Custom theme updates (Updates need to be served from WordPress.org).
  • Custom favicon. Open the header.php file and make sure that there is no hard coded favicon.
  • Custom navigation.
  • Hard coding search forms instead of using get_search_form(), searchform.php or filters.

Tutorial:
Search for:

  • function (remember the space afterwards)
  • class (remember the space afterwards)

Top ↑

Child themes Child themes

Themes must be able to have child themes made from them.

Note: We are working on this section.