How to Properly Comment Your Themes and Plugins

If you purchase through a link on our site, we may earn a commission. Learn more.

One of the marks of a truly great WordPress developer, is his ability to include excellent commenting in his code. Go ahead, take a look at some of the plugins/themes by the best developers, and see how easy it is to read through their code. They adhere to the WordPress Coding Standards when writing HTML and PHP. There is also the CSS Coding Standards page on the Core Contributor Handbook, which is definitely another page to check out. Today we will therefore look at the subject of commenting. I'm going to show you a few examples from top plugins and themes. We'll be focusing on the use of commenting in the documentation of files and functions/classes, but we'll also mention the normal comments you can sprinkle throughout your code.
Table of Contents
WP Engine High Performance Hosting
BionicWP Hosting

One of the marks of a truly great WordPress developer, is his ability to include excellent commenting in his code. Go ahead, take a look at some of the plugins/themes by the best developers, and see how easy it is to read through their code. They adhere to the WordPress Coding Standards when writing HTML and PHP. There is also the CSS Coding Standards page on the Core Contributor Handbook, which is definitely another page to check out.

Today we will therefore look at the subject of commenting. I’m going to show you a few examples from top plugins and themes. We’ll be focusing on the use of commenting in the documentation of files and functions/classes, but we’ll also mention the normal comments you can sprinkle throughout your code.

Documentation Comment Blocks

The purpose of the first batch of comments we’re going to look at is primarily documentation. There are scripts like phpDocumentor that can automatically generate documentation for your code, provided you use a set of standard comments at the start of your files and before every class/function.

Let’s take a look at some examples of PHPDoc blocks. One of my favourite plugin authors is Justin Tadlock, as he truly takes great care in not only writing quality code, but also format the code properly and include helpful comments.

Here’s a snippet from his Custom Content Portfolio plugin:

[php]
/**
* Various functions, filters, and actions used by the plugin.
*
* @package CustomContentPortfolio
* @subpackage Includes
* @since 0.1.0
* @author Justin Tadlock <justin@justintadlock.com>
* @copyright Copyright (c) 2013, Justin Tadlock
* @link
* @license https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/
[/php]

And now a few examples from the Genesis theme/framework. I am truly in love with this framework, and in my opinion every WordPress developer should have a copy of it. Even if you decide not to use it to build themes, you will still learn a lot from looking at the code, it’s absolutely top notch. It’s also been audited by Mark Jaquith, one of the WordPress core developers,

This is the file header of Genesis framework’s index.php file:

[php]
<!–?php <br ?–>/*
WARNING: This file is part of the core Genesis framework. DO NOT edit
this file under any circumstances. Please do all modifications
in the form of a child theme.
*/

/**
* Index template, initialises Genesis.
*
* This file is a core Genesis file and should not be edited.
*
* @category Genesis
* @package Templates
* @author StudioPress
* @license https://www.opensource.org/licenses/gpl-license.php GPL v2.0 (or later)
* @link https://www.studiopress.com/themes/genesis
*/
[/php]

Let’s take a look at a random function from Genesis:

[php]
add_action( ‘after_setup_theme’, ‘genesis_seo_compatibility_check’, 5 );
/**
* Checks for the existence of popular SEO plugins and disables
* the Genesis SEO features if one or more of the plugins is active.
*
* Runs before the menu is built, so we can disable SEO Settings menu, if necessary.
*
* @since 1.2.0
*
* @uses genesis_detect_seo_plugins() Detect certain SEO plugins
* @uses genesis_disable_seo() Disable all aspects of Genesis SEO features
*
* @see genesis_default_title()
*/
function genesis_seo_compatibility_check() {
[/php]

Notice also that the add_action() function call comes exactly before the documentation comments for that function. This is the style which I have also adopted in my plugins. Of course, you can place the add_action() anywhere you want, and other developers prefer to place it beneath the function for example. However I think this it looks visually good and is also very readable for a developer.

Here’s an example function declaration from Easy Digital Downloads:

[php]
/**
* Stores the tax info in the payment meta
*
* @access public
* @since 1.3.3
* @param $payment_meta array The meta data to store with the payment
* @param $payment_data array The info sent from process-purchase.php
* @return array
*/

function edd_record_taxed_amount( $payment_meta, $payment_data ) {
[/php]

As we mentioned earlier, comments are not only used at the beginning of a file or before a function, indeed their main use is to clarify code wherever it is. So I encourage you to use comments throughout your plugin.

Usage of @since

The tag @since is used to document revisions, as in “This function has been a part of this package since version 2.0”.

When you’re building your plugin, the contents of your function might change from one version to the other. This does not really affect the tag as long as the function still does the same job.

For example, if foo() fetches some dataset, you might rewrite the function to return the data from a cache instead of running expensive database queries every time the function is called. The code consuming your API doesn’t care how the data is fetched, only that you provide the correct data. Of course, you might note the change in the function’s description: “Since vX, this function caches the returned dataset. See {@link clearFoo()} if you need to clear the cached data.

You might even use the syntax below for showing new additions to the function, for example changes to the parameters it accepts

[php]
* @since 2.0 foo() introduced
* @since 2.7 foo($arg) added the $arg argument to the signature
* @since 2.9 foo($arg = ‘default’) set a default for $arg
[/php]

Usage of @package and @subpackage

You will see many plugins making use of the @package tags in their function comments. What do they mean? These two tags are just used to organise your own work, and they have no strict application in WordPress plugins.

Let’s take some example usages and you can then decide which one you like best.

Since Justin Tadlock’s code is one of the most organised I’ve come across, I’ll again be taking his Hybrid Core framework as an example. In this framework, @package is used to refer to the plugin in general, while @subpackage is used to refer to the folder.

Here’s an excerpt from the hybrid-core/functions/menus.php file:

[php]
<?php
/**
* The menus functions deal with registering nav menus within WordPress for the core framework. Theme
* developers may use the default menu(s) provided by the framework within their own themes, decide not
* to use them, or register additional menus.
*
* @package HybridCore
* @subpackage Functions
[/php]

Lets take a look at another plugin, Easy Digital Downloads, which is also well documented. Here we see the tags used a bit differently.

The following snippet is from the file Easy-Digital-Downloads/includes/payments/functions.php

[php]
<?php
/**
* Payment Functions
*
* @package Easy Digital Downloads
* @subpackage Payment Functions
[/php]

As I said, both can be good usages of the tags, I just wanted to point out that they are indeed used in different ways by different developers.

Deprecated functions

When you deprecate a function, it’s good practice not to just remove it altogether from your plugin/theme.

For example, in Easy Digital Downloads, we find a file containing all the deprecated functions (includes/deprecated-functions.php). We can then use the @deprecated tag for each function:

[php]
/**
* Get Download Sales Log
*
* Returns an array of sales and sale info for a download.
*
* @param $download_id INT the ID number of the download to retrieve a log for
* @param $paginate bool whether to paginate the results or not
* @param $number int the number of results to return
* @param $offset int the number of items to skip
*
* @access public
* @deprecated 1.3.4
* @since 1.0
* @return array
*/
[/php]

In the code above, notice also that Pippin indents the comments in such a way that the contents of each tag line up with each other. Again, this makes it more readable for himself and other developers contributing to the project.

You can also see another example of a deprecated functions file in Justin Tadlock’s Hybrid Core (functions/deprecated.php):

[php]
<!–?php <br ?–>/**
* Deprecated functions that should be avoided in favor of newer functions. Also handles removed
* functions to avoid errors. Developers should not use these functions in their parent themes and users
* should not use these functions in their child themes. The functions below will all be removed at some
* point in a future release. If your theme is using one of these, you should use the listed alternative or
* remove it from your theme if necessary.
*
* @package HybridCore
* @subpackage Functions
* @author Justin Tadlock <justin@justintadlock.com>
* @copyright Copyright (c) 2008 – 2012, Justin Tadlock
* @link
* @license https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/

/**
* @since 0.2.0
* @deprecated 0.7.0
*/
function hybrid_after_single() {
_deprecated_function( __FUNCTION__, ‘0.7’, "do_atomic( ‘after_singular’ )" );
hybrid_after_singular();
}
[/php]

Note that Justin encourages developers to stop using the old functions. However, not to break things, the functions are still available in this file. Where there is a replacement function, it is referred to in the code, and called from within the deprecated function. Else, if there is no replacement, the original code is kept within the function.

Testing Code

Here’s another handy technique for testing new code.

[php]
/**/
echo(‘This is just experimental code’);
/**/
[/php]

Those comments don’t really do anything for now, and the experimental code executes. Typically with experimental code, we want to try our plugin with the code activated, and with it deactivated. So to deactivate this experimental code, we just need to remove the second backslash, resulting in the experimental code being commented out:

[php]
<!–?php <br ?–>/**
echo(‘This is just experimental code’);
/**/
[/php]

Multi line and Single Line Comments

PHP has three types of comment syntax/* */ which serves as block comments, and // as well as # which are used for inline comments.

A quick reference:

[php]
<!–?php // This comment only spans one line. // It can, however, be used on as many lines as necessary. # This comment only spans one line. # It is not used as commonly as the previous type. /* This comment can span one line. */ /* This comment can also span as many lines as needed. It is useful when commenting out large chunks of code at a time. This type of comment cannot be nested, or errors will occur. */ ?–>
[/php]

If you take a look at the code for WordPress itself, you’ll find that the use of commenting is not standardised, see this example (wp-admin/admin-ajax.php):

[php]
// Require an action parameter
if ( empty( $_REQUEST[‘action’] ) )
die( ‘0’ );

/** Load WordPress Administration APIs */
require_once( ABSPATH . ‘wp-admin/includes/admin.php’ );
[/php]

Why, you might ask? Well, with WordPress being a collaborative effort, these things are bound to happen. To be sure, this is something very minor, but I’d rather have things standardised, if anything to serve as reference for new developers.

WordPress developers all have their own preferences, lets take a look at some examples.

Here’s a snippet from the Bulk User Management plugin, from Automattic. They are using the // commenting type for multiple lines.

[php]
foreach ( $userids as $userid ) {
// The new role of the current user must also have the promote_users cap or be a multisite super admin,
// so make sure `$role` can still promote users if the current user is in `$userids`
if ( $userid == $current_user->ID && ! $wp_roles->role_objects[ $role ]->has_cap(‘promote_users’)
[/php]

Here’s another example from the Soliloquy slider plugin, the author in this case using the /** comment */ format:

[php]
/** Check if slider ID is an integer or string */
if ( is_numeric( $id ) )
$id = absint( $id );
else
$id = esc_attr( $id );
[/php]

Genesis uses /** **/ for single line comments:

[php]
/** Remove standard post content output **/
remove_action( ‘genesis_post_content’, ‘genesis_do_post_content’ );
[/php]

I prefer using the /* */ commenting syntax for multi-line (and even single line) comments, reserving the // syntax for comments that go right next to a line of code (not on a separate line)

Multi line:

[php]
function wprss_limit_words( $words, $limit, $append = ” ) {
/* Add 1 to the specified limit becuase arrays start at 0 */
$limit = $limit + 1;
/* Store each individual word as an array element
up to the limit */
$words = explode( ‘ ‘, $words, $limit );
/* Shorten the array by 1 because that final element will be the sum of all the words after the limit */
array_pop( $words );
/* Implode the array for output, and append an ellipse */
$words = implode( ‘ ‘, $words ) . $append;
/* Return the result */
return rtrim( $words );
}
[/php]

Single line:

[php]
/* Define framework, parent theme, and child theme constants. */
add_action( ‘after_setup_theme’, array( &$this, ‘constants’ ), 1 );

/* Load the core functions required by the rest of the framework. */
add_action( ‘after_setup_theme’, array( &$this, ‘core’ ), 2 );
[/php]

In-line:

[php]
wp_head(); // we need this for plugins
[/php]

Something to keep in mind is that /* .. */ overrides // in PHP commenting.

Sublime Text Addons

If you’re using the Sublime Text 2 editor (seriously, check it out!), you will also find these two packages very handy:

My absolute favorite references for best practices in commenting and plugin structure are Genesis and Hybrid Core, so I definitely encourage to check those out and follow the way they do things.

If you enjoyed this post, make sure to subscribe to WP Mayor’s RSS feed.

Jean Galea

Jean Galea is an investor, entrepreneur, and blogger. He is the founder of WP Mayor, the plugins WP RSS Aggregator and Spotlight, as well as the Mastermind.fm podcast. His personal blog can be found at jeangalea.com.

Discover more from our archives ↓

Popular articles ↓

7 Responses

  1. I don’t even know how I ended up here, but I thought this post was great.
    I don’t know who you are but certainly you’re going to a famous blogger if you are not already 😉
    Cheers!

  2. Great article Jean with examples of the best guys out there.

    One thing that goes a little bit inline with commenting is to add comments at closing divs, so you can see where it refers to.

    So for example: on line 15 you have “while ( have_posts() ) {“, then on line 75 you have “} // endwhile”

    1. Yes Piet, that is also good practice. Some developers (I think Tom McFarlin is one of them) like to comment every closing brace, and I did that for a while, but in the end I felt it was quite a hassle and didn’t really add much value. So now I reserve it only for long functions when I feel it would really aid the person reading the code.

  3. nice post for wp specific comments, as with any dev work, enough, relevant comments to allow other contrubutors to make sense of your code is key.

  4. Great article. I will definitely be following these standards to improve my commenting/documentation!

Share Your Thoughts

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

Claim Your Free Website Tip 👇

Leave your name, email and website URL below to receive one actionable improvement tip tailored for your website within the next 24 hours.

"They identified areas for improvement that we had not previously considered." - Elliot

By providing your information, you'll also be subscribing to our weekly newsletter packed with exclusive content and insights. You can unsubscribe at any time with just one click.