1. Code
  2. PHP

Writing Maintainable WordPress Widgets: Part 2 of 2

Scroll to top
10 min read

In the first post in this series, we discussed the reasons that WordPress Plugins should be treated more like larger software projects (but often aren't) and made a case for using well-organized code in an attempt to make our plugin projects more maintainable. All of this was done within the context of developing a WordPress widget.


The thing is, widgets are not the only type of plugins that can be developed for WordPress. The application also supports plugins that extend its functionality through the use of hooks located throughout the application. As such, widgets are not the only type of plugin that could benefit from a boilerplate.

In this tutorial, we'll define what exactly WordPress hooks are, how they work, and why they are beneficial. Next, we'll take a look at my WordPress Widget Boilerplate and how to leverage it in new projects through the context of an example application.


Understanding Hooks, Actions, and Filters

Before developing plugins, it's important to understand WordPress' model for hooking into the application and the different between actions and filters.

  • Hooks are areas in the core WordPress application that allow you to register your code for execution. Simply put, your plugin registers itself with WordPress at a specific point of execution. When WordPress reaches that point of execution (either in saving information, rendering information or some other point), it fires your code.
  • Actions are a type of hook that WordPress makes available for you to take advantage of whenever a specific event occurs during the application's life cycle. Some of these points include whenever a theme is activated, whenever a post is saved, or whenever style sheets are being rendered. Generally speaking, if you're looking to insert some type of custom functionality at any point in the WordPress life cycle, there is likely an action hook available.
  • Filters are a type of hook that WordPress makes available for you to manipulate data before sending it to the database or sending it to render in the browser. Simply put, WordPress will pass the content to your function and then continue with its process with whatever you return. If you're looking to manipulate data before saving it or seeing it, your best option is to use filter.

Easy enough, right? On top of that, WordPress has solid documentation for adding actions[1] and adding filters[2]. You can reach much more about plugins in the Plugin API[3] in the WordPress Codex[4].


A Plugin Boilerplate

If you read the previous article, then you are already familiar with the Widget API. It's precise in that it requires a constructor and no less than three functions to get something working. Because plugins have the flexibility of hooking into a variety of points in the WordPress process, the Plugin API is a bit less structured. Fortunately, this doesn't mean that we can't craft some form of boilerplate off of which to create our plugins.

After all, they still consist of some common functionality:

  • Core plugin code
  • Style sheets
  • JavaScript
  • Localization files
  • Markup
  • Images

Similar to our WordPress Widget Boilerplate, we can setup our template directory to look like this:

Looks familiar, doesn't it? The most important aspect of the Plugin Boilerplate isn't the details of the directory structure (though we will examine a bit in the future) but the organization of the core plugin code itself.

The Plugin Skeleton

Plugin's can be developed either by using a handful of functions or by wrapping each function in a class in an object-oriented approach. I'm a fan of the latter and my boilerplate represents that. Here's the plugin definition:

1
2
<?php
3
/*

4
Plugin Name: TODO

5
Plugin URI: TODO

6
Description: TODO

7
Version: 1.0

8
Author: TODO

9
Author URI: TODO

10
License:

11


12
    Copyright 2011 TODO (email@domain.com)

13


14
    This program is free software; you can redistribute it and/or modify

15
    it under the terms of the GNU General Public License, version 2, as 

16
    published by the Free Software Foundation.

17


18
    This program is distributed in the hope that it will be useful,

19
    but WITHOUT ANY WARRANTY; without even the implied warranty of

20
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

21
    GNU General Public License for more details.

22


23
    You should have received a copy of the GNU General Public License

24
    along with this program; if not, write to the Free Software

25
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

26
    

27
*/
28
29
// TODO: rename this class to a proper name for your plugin

30
class TODO {
31
	 
32
	/*--------------------------------------------*

33
	 * Constructor

34
	 *--------------------------------------------*/
35
	
36
	/**

37
	 * Initializes the plugin by setting localization, filters, and administration functions.

38
	 */
39
	function __construct() {
40
	
41
    // Define constnats used throughout the plugin

42
    $this->init_plugin_constants();
43
  
44
		load_plugin_textdomain(PLUGIN_LOCALE, false, dirname(plugin_basename(__FILE__)) . '/lang');
45
		
46
    /*

47
     * TODO:

48
     * Define the custom functionality for your plugin. The first parameter of the

49
     * add_action/add_filter calls are the hooks into which your code should fire.

50
     *

51
     * The second parameter is the function name located within this class. See the stubs

52
     * later in the file.

53
     *

54
     * For more information: 

55
     * https://codex.wordpress.org/Plugin_API#Hooks.2C_Actions_and_Filters

56
     */
57
    add_action('TODO', array($this, 'action_method_name'));
58
    add_filter('TODO', array($this, 'filter_method_name'));
59
		
60
		} // end if

61
62
	} // end constructor

63
	
64
	/*--------------------------------------------*

65
	 * Core Functions

66
	 *---------------------------------------------*/
67
	
68
  /**

69
   * Note:  Actions are points in the execution of a page or process

70
   *        lifecycle that WordPress fires.

71
   */
72
	function action_method_name() {
73
    // TODO define your action method here

74
	} // end action_method_name

75
	
76
  /**

77
   * Note:  Filters are points of execution in which WordPress modifies data

78
   *        before saving it or sending it to the browser.

79
   */
80
  function filter_method_name() {
81
    // TODO define your filter method here

82
	} // end filter_method_name

83
  
84
	/*--------------------------------------------*

85
	 * Private Functions

86
	 *---------------------------------------------*/
87
   
88
  /**

89
   * Initializes constants used for convenience throughout 

90
   * the plugin.

91
   */
92
  private function init_plugin_constants() {
93
    
94
    /* TODO

95
     * 

96
     * This provides the unique identifier for your plugin used in

97
     * localizing the strings used throughout.

98
     * 

99
     * For example: wordpress-widget-boilerplate-locale.

100
     */
101
    if(!defined('PLUGIN_LOCALE')) {
102
      define('PLUGIN_LOCALE', 'plugin-name-locale');
103
    } // end if

104
    
105
    /* TODO

106
     * 

107
     * Define this as the name of your plugin. This is what shows

108
     * in the Widgets area of WordPress.

109
     * 

110
     * For example: WordPress Widget Boilerplate.

111
     */
112
    if(!defined('PLUGIN_NAME')) {
113
      define('PLUGIN_NAME', 'Plugin Name');
114
    } // end if

115
    
116
    /* TODO

117
     * 

118
     * this is the slug of your plugin used in initializing it with

119
     * the WordPress API.

120
     

121
     * This should also be the

122
     * directory in which your plugin resides. Use hyphens.

123
     * 

124
     * For example: wordpress-widget-boilerplate

125
     */
126
    if(!defined('PLUGIN_SLUG')) {
127
      define('PLUGIN_SLUG', 'plugin-name-slug');
128
    } // end if

129
  
130
  } // end init_plugin_constants

131
	
132
	/**

133
	 * Helper function for registering and loading scripts and styles.

134
	 *

135
	 * @name	The 	ID to register with WordPress

136
	 * @file_path		The path to the actual file

137
	 * @is_script		Optional argument for if the incoming file_path is a JavaScript source file.

138
	 */
139
	private function load_file($name, $file_path, $is_script = false) {
140
		$url = WP_PLUGIN_URL . $file_path;
141
		$file = WP_PLUGIN_DIR . $file_path;
142
		if(file_exists($file)) {
143
			if($is_script) {
144
				wp_register_script($name, $url);
145
				wp_enqueue_script($name);
146
			} else {
147
				wp_register_style($name, $url);
148
				wp_enqueue_style($name);
149
			} // end if

150
		} // end if

151
	} // end _load_file

152
  
153
} // end class

154
// TODO: update the instantiation call of your plugin to the name given at the class definition

155
new TODO();
156
?>

Most IDE's have a function that lists all outstanding TODO's, so I place them throughout the code to easily locate what needs to be done during development. Note that there are three primary areas of code:

  1. Constructor. This function is responsible for defining any constants used throughout the code, specifying any localization files, and registering all actions and filters with WordPress.
  2. Core Functions are the actual function definitions registered in the constructor that are fired after during WordPress' execution.
  3. Helper Functions refer to functions that I use that help in execution by abstracting away common functionality (such as registering JavaScripts and stylesheets).

Note here that plugin development deviates from widget development in that there aren't multiple functions that are expected. In fact, you really only need a constructor. From there, any functions defined within the add_action or add_filter calls and your responsibility to implement.

Make sense? Let's take a look at using this boiler plate in a simple example.


A Working Example With Your Blog's RSS Feed

WordPress offers hooks for almost point of execution that you can imagine. In this example, we'll hook into the post rendering process in order to introduce a custom message. The thing is, we only want to display said message within the context of an RSS reader.

First, the requirements:

  • Display a message at the footer of each post
  • The message should only appear in RSS readers
  • The message should contain a link back to the website of the post

Based on this, it doesn't look like we'll need to use any JavaScript, CSS, or markup create our plugin so we can reduce our plugin boilerplate to the core plugin code and the localization files:

At this point, we can begin stubbing out the boilerplate with a name and the plugin constants:

1
2
  /**

3
   * Initializes constants used for convenience throughout 

4
   * the plugin.

5
   */
6
  private function init_plugin_constants() {
7
  
8
    if(!defined('PLUGIN_LOCALE')) {
9
      define('PLUGIN_LOCALE', 'rss-note-locale');
10
    } // end if

11
    
12
    if(!defined('PLUGIN_NAME')) {
13
      define('PLUGIN_NAME', 'RSS Note');
14
    } // end if

15
16
    if(!defined('PLUGIN_SLUG')) {
17
      define('PLUGIN_SLUG', 'rss-note-slug');
18
    } // end if

19
  
20
  } // end init_plugin_constants

Next, we need to consider what type of hook is required for manipulating the content. Recall tha tsince we're trying to add something to the content proir to rendering it in the browser, we'll need a filter. From here, we can stub out the constructor:

1
2
	/**

3
	 * Initializes the plugin by setting localization, filters, and administration functions.

4
	 */
5
	function __construct() {
6
	
7
    // Define constnats used throughout the plugin

8
    $this->init_plugin_constants();
9
  
10
		load_plugin_textdomain(PLUGIN_LOCALE, false, dirname(plugin_basename(__FILE__)) . '/lang');
11
		
12
    // add the note to both the excerpt and the main feed

13
    add_filter('the_content', array($this, 'display_rss_note'));
14
    add_filter('the_excerpt_rss', array($this, 'display_rss_note'));
15
16
	} // end constructor

Note that the function is using two filter - one for the_content[5] and one for the_excerpt_rss[6]. We're doing this because some users opt to only publish an excerpt of their blog rather than all of the content and we want to make sure that we're capturing both cases.

Next, let's actually implement the function definition that will append the message to the rest of the post:

1
2
  /**

3
   * Appends a short message at the footer of each post viewed in an RSS reader

4
   * reminding users to visit your site.

5
   */
6
  public function display_rss_note($content) {
7
    
8
    if(is_feed()) {
9
    
10
			$content .= '<div class="rss-note">';
11
				$content .= '<p>';
12
          $content .= __('Thanks for reading! Be sure to catch up on the rest of my posts at ', PLUGIN_LOCALE);
13
          $content .= '<a href="' . get_bloginfo('url') . '">';
14
            $content .= get_bloginfo('name');
15
          $content .= '</a>!';
16
        $content .= '</p>';
17
			$content .= '</div>';
18
			
19
		} // end if

20
		
21
		return $content;
22
    
23
	} // end display_rss_note

Note here that the function accepts a parameter referred to by the $content variable. WordPress itself is passing this data into the function. For these particular filters, we're dealing with the content of a blog post so whatever we add to it must be concatenated so that it's added to the end of it.

This message we're adding simply says "Thanks for reading! Be sure to catch up on the rest of my posts at [Blog Name]!" through use of the get_bloginfo()[7] function.. Of course, you can update it to read whatever you like. Finally, note that we've wrapped this in an conditional that checks the is_feed()[8] function. This is important as we only want this code to fire if the content is being sent via an RSS feed.

That's it - not too bad, right? You can download the full version of the working source code (including the associated README) for this plugin on GitHub or right here from Wptuts. The boilerplate is also available on GitHub, too.

The point of this series was not only to help provide an introductory guide to the WordPress Plugin API, but also to provide a case and boilerplates for making it much easier to treat and maintain WordPress Plugins like any other software project.

  1. http://codex.wordpress.org/Function_Reference/add_action
  2. http://codex.wordpress.org/Function_Reference/add_filter
  3. http://codex.wordpress.org/Plugin_API
  4. http://codex.wordpress.org/Main_Page
  5. http://codex.wordpress.org/Function_Reference/the_content
  6. http://codex.wordpress.org/Template_Tags/the_excerpt_rss
  7. http://codex.wordpress.org/Function_Reference/is_feed
  8. http://codex.wordpress.org/Function_Reference/get_bloginfo
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.