3. WordPress Plugin Structure

When we plan to develop and release a production level WordPress Plugin, we have to follow certain best practices suggested by WordPress and also by other accomplished Plugin developers. WordPress Plugin structure as to follow some well defined layout.

In this section, we show how to structure a WordPress Plugin and also highlight some of the coding standards that makes the plugin easy to maintain and increase readability.

Share on Social Plugin.

Here onwards, we go through Share on Social Plugin project. Download the complete Share on Social Project from GitHub which contains plugin code and also the unit tests.

It is an Eclipse ready project which we can import to Eclipse as a existing project. But, you can also use it with any other PHP IDE.

Enable WP_DEBUG

WordPress strongly recommends to enable WP_DEBUG while developing themes and plugins. When enabled, WordPress outputs all notices, warnings and errors to a log file debug.log in wp-content directory. Debug log catches many subtle warning and errors which otherwise may go unnoticed by the developers.

To enable debug, edit WordPress configuration file wp-config.php and add following lines.

/opt/lampp/htdocs/wordpress-4.0.1/wp-config.php

/**
 * For developers: WordPress debugging mode.
 *
 * Change this to true to enable the display of notices during development.
 * It is strongly recommended that plugin and theme developers use WP_DEBUG
 * in their development environments.
 */

@ini_set(‘display_errors’,0);
define('WP_DEBUG', true);
define('WP_DEBUG_DISPLAY', false);
define('WP_DEBUG_LOG', true);
define('SAVEQUERIES', true);


/* That's all, stop editing! Happy blogging. */

To test proper WP_DEBUG configuration, place a error_log('some message'); in share-on-social.php and open the site in the browser. WordPress outputs the message to the log file/opt/lampp/htdocs/wordpress-4.0.1/wp-content/debug.log.

Keep tab on debug.log

During development, check the debug.log regularly and fix the errors and warnings When done so, it is quite easy to fix the errors and keep the plugin healthy.

In case we plan to host the plugin on wordpress.org, approval may not happen when there are unfixed warnings and errors in the log.

Define constants

Throughout the plugin, we often refer some paths and constants and normally, they are defined at the start of plugin in the main file. In Share on Social, we define them in its main file share-on-social.php

share-on-social/share-on-social.php

define( 'SOS_NAME', 'share-on-social' );
define( "SOS_VERSION", '1.0.0' );

define( "SOS_URL", trailingslashit( plugin_dir_url( __FILE__ ) ) );

// plugin path
define( "SOS_PATH", plugin_dir_path( __FILE__ ) );

//basename share-on-social/share-on-social.php
define( "SOS_PLUGIN_BASENAME", plugin_basename( __FILE__ ) );

// main file - share-on-social.php
define( "SOS_PLUGIN_FILE", __FILE__ );

We start all constants with SOS_ so that names don’t clash with constants from other themes and plugins. Always prefix the constants with the plugin name or some unique abbreviation.

 
 

Functions and Class Files

It is easy to code the plugin functionality with PHP functions. But this approach may results in function name conflict either with functions defined by site theme and other active plugins or even with WordPress API.

Even though bit difficult, better approach is to use PHP Classes which allow us name the methods without the name conflict.

For Share on Social, we use classes for all modules except the main plugin file. In the main file, we use regular functions. Rest of the functionality are split into class files which are included in main file.

Let’s see how this is done with two class files; one for front end and another for admin module. In the following code snippet, we include two class files class-frontend.php and class-admin.php.

share-on-social/share-on-social.php

// plugin entry point
setup_sos_plugin();

function setup_sos_plugin () {

    require_once SOS_PATH . 'include/class-helper.php';
    
    if ( is_admin() ) {
       
        require_once SOS_PATH . 'admin/class-admin.php';
        add_action( 'plugins_loaded', 'load_sos_admin' );

    } else {

        require_once SOS_PATH . 'frontend/class-frontend.php';
        add_action( 'plugins_loaded', 'load_sos_frontend' );

    }
}

function load_sos_admin () {
    $sos_admin = new Sos_Admin();
    $sos_admin->setup();
}


function load_sos_frontend () {
    $sos_frontend = new Sos_Frontend();
    $sos_frontend->setup();
}
  • In the main file, we call a single function setup_sos_plugin() which is also the starting point of the plugin.

  • In the function, we check whether we are in admin screen or in site with the help of is_admin(). WordPress function is_admin() checks whether we are in the Admin panel. Please note that we should not use this function to check whether the user has admin privileges or not.

  • If we are in admin screen, then include class-admin.php and then attach load_sos_admin() function to WordPress hook plugins_loaded using add_action(). We explain add_action() in the next blog but for the time it is suffice to note that when all active plugins are loaded, WordPress calls the function load_sos_admin().

  • In load_sos_admin() function, we create a object of class Sos_Admin and call its setup() method.

  • We do similarly with class-frontend.php, if is_admin() is false.

It is important to note that load_sos_admin() and load_sos_frontend() are not called immediately by add_action in setup_sos_plugin(), but their execution is deferred. Just for the sake of explanation, we split this into two phases. When we access the site, as part of phase one, WordPress processes all activated plugins. When share-on-social.php is processed its setup_sos_plugin() function is called. In this function, WordPress includes the class files and calls add_action() which just registers load_sos_admin() and load_sos_frontend() as callback functions with WordPress for a hook named plugins_loaded. Then WordPress goes on to process other plugins which are active in the site. When all active plugins are processed, WordPress triggers second phase by calling the hook plugins_loaded. At this point, WordPress calls registered callback methods load_sos_admin() and load_sos_frontend() and execute code contained in them.

Next, let’s go through a plugin class file to see how classes are defined with a snippet from class-frontend.php .

share-on-social/frontend/class-frontend.php

<?php

defined( 'ABSPATH' ) or die( "Access denied !" );

class Sos_Frontend {

    var $sos_shortcode = 'share-on-social';

    public function setup () {
        
        // enable shortcode
        add_shortcode( $this->sos_shortcode, 
                array(
                        $this,
                        'enable_sos_shortcode'
                ) );
    }


    // other methods of the class

}

In the class file, we define class Sos_Frontend and its method. First define variables which have class scope such as $sos_shortcode. In class, we use setup() to carry out initializations tasks such as add actions, filters, shortcodes and also to include other class files and modules etc., All class files of the plugin follow similar pattern. At this stage, it is enough to comprehend the overall structure of plugin and its files as we cover the code in detail throughout the tutorial.

WordPress Plugin Structure

The layout or directory structure of the plugin is shown in the screenshot.

WordPress Plugin Structure - Share on Social Plugin layout

  • top level directory - contains main file share-on-social.php and also uninstall.php which is used to uninstall the plugin. It also contains a build file to zip the plugin, unit test configuration files. The file readme.txt is mandatory when we plan to host the plugin on http://wordpress.org/plugins directory.

  • admin - directory holds class files related to admin module.

  • css - contains CSS files of the plugin.

  • frontend - contains front end class files.

  • images - directory to hold images and icons used by the plugin.

  • include - helper and util classes used by plugin.

  • js - holds JavaScript files.

  • langs - language translation files for internationalization.

  • tests - phpunit and qunit test files.

  • dev - files in this directory are not part of the actual plugin but related to development process such as Eclipse Code Formatter for WordPress and other stuff related to developments.

There is no hard and fast rules about directory structure. We just took it from other leading plugins and developer is free to adjust the structure as required.

 
 

File and variable names

WordPress recommends Coding Standard to follow while contributing to WordPress core. Even though they are not mandatory for plugins, we prefer to use them for plugin development to improve the readability of the code.

We use - (dash) in file names as in class-admin.php and prefix class- to files to denote that they are class files. For variables, functions and methods, we use _ (underscore) as in $share_name. In JavaScript, for functions and variables we use camel case as in processLockers().

Eclipse Formatter Profile.

WordPress PHP Coding standard recommends a list of formatting standard like quotes, indentation etc., Projects’ dev directory contains Eclipse-Formatter-PHP-for-Wordpress.xml which sets many of the recommendations.

If you are using Eclipse, you may import the profile using Project PropertiesCode StyleFormatterImport.

In the next chapter, we go through plugins main file and also explain the benefits of PHP Classes.