WordPress Plugin Boilerplate, Part Two

In a previous post, I introduced WordPress Plugin Boilerplate (hereafter referred to as WPBB for brevity), an attempt at standardization for the way WordPress plugins are developed. While it takes a little getting used to, I’ve found it to be a great foundation for developing plugins.

In this post we’ll take a high-level look at the structure of the WPPB and talk about which files you should zero in on when you’re adding your own code.

The Generator

The easiest way to start your boilerplate is to use the WordPress Plugin Boilerplate generator site. Simply enter some basic information about the plugin you’re building and the site will provide you with a custom .zip file into which you can begin developing your new masterpiece.

The WordPress Plugin Boilerplate Generator

Folder Structure

Let’s start with an overview of the boilerplate’s folder structure:

the WordPress Plugin Boilerplate folder structure

I named my plugin “Boilerplate Tutorial,” so as you can see (at the bottom of the image above) we have a “boilerplate-tutorial.php” file, the obligatory base file that is needed with all WordPress plugins. In the case of the WPPB, the file isn’t something you’re likely to have a lot of reason to modify. Out of the box, it looks like this:

<?php

/**
 * The plugin bootstrap file
 *
 * This file is read by WordPress to generate the plugin information in the plugin
 * admin area. This file also includes all of the dependencies used by the plugin,
 * registers the activation and deactivation functions, and defines a function
 * that starts the plugin.
 *
 * @link              https://lampstack.ninja
 * @since             1.0.0
 * @package           Boilerplate_Tutorial
 *
 * @wordpress-plugin
 * Plugin Name:       Boilerplate Tutorial
 * Plugin URI:        https://lampstack.ninja
 * Description:       This is a short description of what the plugin does. It's displayed in the WordPress admin area.
 * Version:           1.0.0
 * Author:            Kenn Kitchen
 * Author URI:        https://lampstack.ninja
 * License:           GPL-2.0+
 * License URI:       http://www.gnu.org/licenses/gpl-2.0.txt
 * Text Domain:       boilerplate-tutorial
 * Domain Path:       /languages
 */

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Currently plugin version.
 * Start at version 1.0.0 and use SemVer - https://semver.org
 * Rename this for your plugin and update it as you release new versions.
 */
define( 'BOILERPLATE_TUTORIAL_VERSION', '1.0.0' );

/**
 * The code that runs during plugin activation.
 * This action is documented in includes/class-boilerplate-tutorial-activator.php
 */
function activate_boilerplate_tutorial() {
	require_once plugin_dir_path( __FILE__ ) . 'includes/class-boilerplate-tutorial-activator.php';
	Boilerplate_Tutorial_Activator::activate();
}

/**
 * The code that runs during plugin deactivation.
 * This action is documented in includes/class-boilerplate-tutorial-deactivator.php
 */
function deactivate_boilerplate_tutorial() {
	require_once plugin_dir_path( __FILE__ ) . 'includes/class-boilerplate-tutorial-deactivator.php';
	Boilerplate_Tutorial_Deactivator::deactivate();
}

register_activation_hook( __FILE__, 'activate_boilerplate_tutorial' );
register_deactivation_hook( __FILE__, 'deactivate_boilerplate_tutorial' );

/**
 * The core plugin class that is used to define internationalization,
 * admin-specific hooks, and public-facing site hooks.
 */
require plugin_dir_path( __FILE__ ) . 'includes/class-boilerplate-tutorial.php';

/**
 * Begins execution of the plugin.
 *
 * Since everything within the plugin is registered via hooks,
 * then kicking off the plugin from this point in the file does
 * not affect the page life cycle.
 *
 * @since    1.0.0
 */
function run_boilerplate_tutorial() {

	$plugin = new Boilerplate_Tutorial();
	$plugin->run();

}
run_boilerplate_tutorial();

About the only thing I ever do with this file is add an autoload statement for Composer (if I’ve used libraries that I’ve added with Composer). As you can see, it hooks into the activation and deactivation classes and loads the overall plugin as a class.

There is an “admin” folder and a “public” folder; respectively, these are for code relating to the dashboard (such as your settings/options screens) and for code supporting the parts of your plugin that display on the public-facing website.

The “Includes” Folder

The “includes” folder has several files. The first is named “class-” plus your plugin slug (for me that’s “class-boilerplate-tutorial.php”), and this is where you load other things that your plugin might need. But wait… this is not where you put your custom Javascript or CSS! The section below will handle loading those, but you’ll reference them elsewhere.

	/**
	 * Register all of the hooks related to the admin area functionality
	 * of the plugin.
	 *
	 * @since    1.0.0
	 * @access   private
	 */
	private function define_admin_hooks() {

		$plugin_admin = new Boilerplate_Tutorial_Admin( $this->get_plugin_name(), $this->get_version() );

		$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
		$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );

	}

	/**
	 * Register all of the hooks related to the public-facing functionality
	 * of the plugin.
	 *
	 * @since    1.0.0
	 * @access   private
	 */
	private function define_public_hooks() {

		$plugin_public = new Boilerplate_Tutorial_Public( $this->get_plugin_name(), $this->get_version() );

		$this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' );
		$this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' );

	}

What you do put here are your own hook and filter references. For instance, say you wanted to register a custom post type. Normally we do that with an init hook, so we could add that here as follows:

	private function define_admin_hooks() {

		$plugin_admin = new Boilerplate_Tutorial_Admin( $this->get_plugin_name(), $this->get_version() );

		$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
		$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );

		// add an init hook that will reference "my_init_function"
		$this->loader->add_action( 'init', $plugin_admin, 'my_init_function');

	}

So this ends up being pretty awesome; every hook and/or filter that your plugin needs will be listed right here in this same spot.

Within this folder you also have class-[plugin-slug]-activator.php and class-[plugin-slug]-deactivator.php, in which you would place your activation- and deactivation-specific tasks. The “i18n” file is for internationalization and finally there’s the class-[plugin-slug]-loader.php (which you’ll probably never need to touch).

The “Admin” and “Includes” Folders

These two sections are virtually identical, so other than what you put there (as in, whether its for the dashboard or the public-facing website), everything we cover going forward applies to either.

Each of these sections has a file named class-[plugin-slug]-public/admin.php, a “css” folder, a “js” folder, and a “partials” folder. The bulk of your PHP code will go in one or the other of these. So, for example, above I added an init hook that referenced a non-existent function called “my_init_function”; I could put that in my class-boilerplate-tutorial-admin.php and it would just work.

The css and js folders come with handy empty(ish) files in which you can deposit your (obviously) CSS and Javascript. The formatting of the default Javascript files prime them for jQuery use, but in both cases, you just drop your code into the files provided and you’re good.

And that’s really the key feature (in my mind) of the whole thing: a place for everything, and when everything’s in its place, it all works swimmingly.

A Beer/Coffee Relationship

I couldn’t find one with beer mugs!

From the first time I heard about it, I thought the WPPB was a great idea. But I didn’t like it. I wanted to like it, but I wasn’t sure what all the pieces where and how I was supposed to use them. In retrospect, I might have been over-thinking it: I was afraid that I was supposed to use it like “that” but I did “this” instead. It was like beer and coffee… my thoughts on my first (and several subsequent) tastes were that they were not for me.

But also like beer and coffee, the WPPB just took a little getting used to. Now I use it for basically every plugin I create. Once I got used to it, it feels very natural and it most certainly saves me time. Likely it would be easier for some subsequent programmer to understand, especially if they were WPPB users themselves.

I hope you’ll try the WPPB (more than once if need be!) and let me know what your experience was like. Love it or hate it, please feel free to comment on your thoughts and experiences.

Leave a Reply

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