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.
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).
- Create my-setup folder inside the
plugins
folder of your WordPress installation. - 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 */
- 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). - Go to WordPress plugin screen (in admin) and activate your plugin there.
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).
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 functionalityfunctions.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.
[tut download=”http://cdn.onextrapixel.com/wp-content/uploads/2012/06/Custom-Functionality-Plugin-for-WordPress.zip”]
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.