Custom Functionality Plugin for WordPress – Pros and Cons

The evolution of WordPress creates a lot of opportunities for developers to make really unique sites and apps. But with uniqueness a problem arises - you have to organise your code wisely and in a future-proof fashion.

Custom Functionality Plugin for WordPress - Pros and Cons

In the following article we are going to discuss the wide-spread practice of storing all custom code in the theme's functions.php file. We'll try to find the point when this approach becomes unhealthy and offer an alternative called Custom functionality plugin.

The Scope of the Problem

When developing with WordPress one always (really, ALWAYS!) has some portion of code that powers the site functionality - theme or plugins tweaks, custom shortcodes, custom post types registrations etc. At the same time there are plenty of useful code snippets, functions, filters etc., that every developer has in their possession and from time to time needs to implement on a particular project.

Regardless of what you're developing - sole personal blog or 101st client's project - you'll need a place for this code. WordPress veterans still remember my-hack.php file. It was silently deprecated since 2.8, but is what has opened the road for the number of alternatives. One of them became extremely popular due to it's simplicity - theme's functions.php file. It's really very simple:

  • every theme has this file as it is required by WordPress rules
  • you should not worry about loading it's code - Core does it for you
  • it loads early enough to hook nearly all your actions and filters
  • you so not need to be a PHP guru to copy/paste a snippet of code found somewhere in the web into the file

So, what's wrong with functions.php? Actually, nothing is wrong with adding code to the theme’s functions.php file if it really belongs to the theme and should be there. That’s not a problem at all. The problem occurs when:

  • You added some code that wasn't tied to the particular theme and then switched the theme
  • You develop sites for clients and have found yourself performing the same cleanup and merger routine for every project
  • You have a bunch of sites to maintain and you found a bug to be fixed in every function.php file of these sites
  • You added some code related to back-end (like metabox callbacks) and it will be loaded at every front-end call also
  • You put a lot of code into this file so it becomes really HUGE

In other words, adding all your custom code into the theme's functions.php does not allow you to:

  • have a modular and maintainable code
  • separate presentation functionality from content-related and back-end one
  • have a default foundation for multiple projects that could be easily updated for all projects

Why should I bother? - if this question comes to your mind, you probably don't have to. Otherwise we'll consider a quite obvious alternative - create a plugin of your own and put the above mentioned code there. The plugin? Why is it better?

Custom Functionality Plugin - Pros and Cons

First of all, I'd like to note that there is some kind of misconception about a plugin as third party extension, created by somebody named plugin developer, who probably has some special qualification for that. This extension should be downloaded from the WordPress plugin repository, updated when the notification appears in adminbar and so on. But in reality it's not true.

If you can edit your functions.php file you are already qualified to create a plugin at least for your own purpose. You don't have to submit your plugin into the WordPress repository if you don't want to, but it should not prevent you from being a master of your own code. The plugin is treated by WordPress in a much more flexible fashion than the theme's functions.php. In particular, it could:

  • be deactivated if it's broken or for debug purposes
  • have it's own structure of files, folders, classes, assets to reflect your need and style of coding
  • depend on other plugins and load itself only after them to prevent errors
  • load some pieces of code only when needed
  • allow you to solve all the above mentioned problems with code portability

A brief look onto discussions on the matter in the WordPress community will bring at least two valuable counter-arguments:

  • I'm developing a site for myself based on my own theme that I am very unlikely to change in future.
  • I'm a well-known premium themes provider. Why should I force my users to download and install some plugin in addition to the theme's installation?

What could be the answer to that? You should know your situation better and choose tools that are best fit for it. There is no suits-for-all decision but let's consider our options anyway.

Where to Start

Custom Functionality (or whatever name you choose) plugin could be created as any other WordPress plugin (refer to Codex for details).

  1. Create my-setup folder inside the plugins folder of your WordPress installation.
  2. Create a PHP file my-setup.php in this folder and put obligatory plugin heading into it.
    /* 
    Plugin Name: My Glorious Setup 
    Description: All of the important functionality and custom modules of the site 
    Version: 0.1 
    Author: Your Name 
    Author URI: yoururl 
    */
    
  3. Create the readme.txt file into the same folder to describe what it does and track the changes. Even for you own usage it's very helpful practice (detailed reading about readme.txt maintenance).
  4. Go to WordPress plugin screen (in admin) and activate your plugin there.

Plugins

Now you have your own plugin working - it does nothing for now but it's ready to store all your tweaks and snippets, your actions and filters, everything that makes your WordPress installation truly unique.

Structuring a Custom Functionality Plugin

First of all let's have some rough idea of what we are going to include in our newly created plugin. There are at least the following types of functionality to consider:

  • content manipulation: registering custom post types and taxonomies, metadata and/or options management, users' capability management etc.
  • admin UI customisation and actions for admin: custom metaboxes, dashboard widgets, custom admin styles, admin menu modifications, options pages etc.
  • functionality required in both front-end and back-end sides: favicon support, formatting functions etc.
  • registration of third-party CSS/JS plugins and/or libraries for future usage in front-end and back-end.
  • functionality that you'd like to use across multiple projects

Now we have come to a very subjective point when there is no plain recipe for what to do - code organisation, files' and folders' structure depend very strongly on your own methods, approaches and practices. Nevertheless a preliminary draft could be constructed right now taking into consideration the following tasks:

  • separate admin code from the front-end
  • have portable portion of code to be used and updated across multiple projects

This draft proposes the following folders/files structure (in the most simplified case).

Folders

As it could be guessed from the names - default folder’s aimed to cross-projects functionality and site folder - to the project specific one. The benefit of such an approach is quite obvious: if you need to change something in default part, you only need to update your file once and then uploaded it into different installations (with necessary testing) leaving the site code untouched. The drawback is that your files structure becomes a bit complicated. But when the number of projects to update numbers at least 10, I bet you would not worry about that. If you see other potential problems please give us your warnings in the comments.

The assets folders are going to carry all your custom scripts, styles and graphics that are not supposed to be in the theme folder, for example:

  • jQuery plugins that are not packed with core and are used in front-end and back-end (they could be registered once and then queued when needed)
  • custom scripts, styles or graphics for back-end only
  • custom graphics for login page

The purposes of files are as follows:

  • admin.php - is going to hold all your admin related functionality
  • functions.php - is going to hold procedural helpers and snippets that you commonly use in code (in front-end and back-end)
  • core.php - this is the place to alternate the default WordPress behaviour in one way or another

The distribution of code between these files is quite a creative process and maybe (and most likely) you’ll finally come up with a completely different structure of your own. But let’s look through some snippets from each proposed type of file to get the idea more clearly.

Code Example from admin.php

/**
 * Load custom CSS in admin
 **/
function my_setup_admin_css(){
    
    $src = MY_SETUP_PLUGIN_URL.'/default/assets/css/my-setup-admin.css';
    
    wp_enqueue_style('my-setup-admin', $src, array(), MY_SETUP_VERSION);
}
add_action('admin_enqueue_scripts', 'my_setup_admin_css');

With this small function we queue custom CSS file to be loaded in back-end. We don't need this piece of code for the visitor of our site in front-end so we included it in admin.php which is loaded only upon admin request.

Code Example from core.php

/**
 * Create custom rewrite rules
 **/
    
function my_setup_custom_rewrite_rules(){		
    global $wp_rewrite, $wp;		
    
    add_rewrite_rule( 'login/?$', 'wp-login.php', 'top' );         
}

add_action('init', 'my_setup_custom_rewrite_rules');

With this function we add a custom rewrite rule to the system (credit for this snippet goes to Ozh). This function has to be hooked into the WordPress loading and parsing process, so we've included it here.

Code Example from functions.php

/**
 * Get current local timestamp
 **/
function my_setup_today_stamp(){
	
	$offset = get_option('gmt_offset');
    return strtotime(sprintf('now %s hours', $offset));
}

This is a small function that returns the today's time stamp taking into consideration GMT offset stored as an option. The functions are independent from any hooks and could be used either in front-end or back-end if needed, so we have put it into generic functions.php.

Now we have come to a very important point where all the magic happens. We are going to load our files to make the plugin work. It could be done by direct require_once call in my-setup.php, but a bit more of a flexible solution is to wrap it into some kind of loader function. Doing this provides us with an alternative - to call the loader directly or apply it to some predefined hook after loading stuff we depend on, eg. other plugins.

Finally our my-setup.php should probably include activation and deactivation hooks and could look like the following:

/*
Plugin Name: My Glorious Setup
Description: All of the important functionality and custom modules of the site
Version: 0.1
Author: Your Name
Author URI: yoururl
*/


/**
 * Define some useful constants
 **/
define('MY_SETUP_VERSION', '0.1');
define('MY_SETUP_PLUGIN_DIR', WP_PLUGIN_DIR . '/my-setup');
define('MY_SETUP_PLUGIN_URL', WP_PLUGIN_URL . '/my-setup');


/**
 * Load files
 **/
function my_setup_load(){
    
    //load core files
    require_once(MY_SETUP_PLUGIN_DIR.'/default/core.php');
    require_once(MY_SETUP_PLUGIN_DIR.'/site/core.php');
    
    //load funcitons files
    require_once(MY_SETUP_PLUGIN_DIR.'/default/functions.php');
    require_once(MY_SETUP_PLUGIN_DIR.'/site/functions.php');
    
    if(is_admin()){
        //load admin related code only upon admin request
        require_once(MY_SETUP_PLUGIN_DIR.'/default/admin.php');
        require_once(MY_SETUP_PLUGIN_DIR.'/site/admin.php');
    }
    
    /* allow dependent */
    do_action('my_setup_loaded'); 
}


/**
 * Call the loader (uncomment only one alternative at a time)
 **/ 
my_setup_load(); //direct load 
/* add_action('plugins_loaded', 'my_setup_load', 5); // wait until other plugins */


/**
 * Activation and Deactivation Hooks
 **/
register_activation_hook(__FILE__, 'my_setup_activation');

function my_setup_activation() {
    //actions to perform once on plugin activation 
	
    flush_rewrite_rules(); //it's very often needed
	do_action('my_setup_activation_actions');
	
}


register_deactivation_hook(__FILE__, 'my_setup_deactivation');

function my_setup_deactivation() {
	// actions to perform once on plugin deactivation
	
    flush_rewrite_rules(); //it's very often needed
	do_action('my_setup_deactivation_actions');
}

You can download the whole plugin's template as a solid package.

Conclusion

This article introduces the custom functionality plugin as a modular and portable alternative to code's copy/pasting into the theme's functions.php file. There are advantages and disadvantages of such an approach, which are discussed from time to time in the WordPress community. But as developers we should consider possible alternatives to choose the best fit for us. Who knows? Maybe this humble draft will one day underlie a new and robust framework for WordPress development - your framework.

Just in case you still need some additional clarifications and arguments, you can go through a further reading list of useful articles from well-known WordPress influencers.

Please share your thoughts and own practices with us in the comments.