WordPress tutorials will often advise you to copy and paste code snippets directly into your theme’s functions.php
file. That might seem harmless enough, but if you keep adding functionality in an arbitrary fashion your code can quickly become unmanageable. In this post I will share some strategies for keeping your functions.php
file tidy and making it easier to maintain.
Keep the global space clean
One problem with the typical functions.php
file – and, it could be argued, with WordPress generally – is that variables and functions are declared in the global scope. That means you risk naming conflicts with functions and variables declared elsewhere in your own code, in plugins, or in the WordPress core.
As an example, suppose you create a function named get_post_slug
in functions.php
. Now let’s imagine that in the future a function with the same name is introduced to the WordPress core. Your function declaration will now throw a fatal error:
Fatal error: Cannot redeclare get_post_slug()
To avoid naming collisions you could prefix your function names to make them unique – for example yourthemename_get_post_slug
– but a more robust approach is to encapsulate your theme’s logic in a class.
A typical hooked function looks like this:
function custom_excerpt_length () {
return 100;
}
add_filter('excerpt_length', 'custom_excerpt_length');
Here is that same functionality encapsulated in a class:
class YourThemeName {
function __construct() {
add_filter('excerpt_length', array($this, 'custom_excerpt_length'));
}
function custom_excerpt_length () {
return 100;
}
}
$yourthemename = new YourThemeName();
Because the custom_excerpt_length
function is a class method it no longer has global scope, and the $yourthemename
variable ensures that our theme’s logic has a single point of access. (Note: You should change YourThemeName
and $yourthemename
to something unique to your theme.)
You might have noticed that the second parameter passed to add_filter
is an array, with $this
as the first value. This tells WordPress to use a class method as a callback. You can read more about the different types of callbacks in the add_filter
documentation.
Theme setup
Most themes have some setup code that needs to run straight away. Let’s add an init
method to our class to handle this job:
class YourThemeName {
function __construct() {
add_action('after_setup_theme', array($this, 'init'));
// Register action/filter callbacks here...
}
function init() {
// All theme initialization code goes here...
register_nav_menus(
array(
'header-menu' => __('Header Menu', 'yourthemename')
)
);
}
// Action/filter callbacks go here...
}
$yourthemename = new YourThemeName();
Any functionality you want hooked to the after_setup_theme
action can go in the init
method. In the example above we registered a navigation menu.
Utility methods
In addition to WordPress action/filter hooks your theme class can also provide utility methods, which can be accessed from templates.
Here is a simple utility method to get the ID of a category by supplying its name:
function get_category_id ($cat_name) {
$term = get_term_by('name', $cat_name, 'category');
return $term->term_id;
}
If you include that method in your theme’s class you can now access it from a template like so:
$yourthemename->get_category_id('category-name');
Group your functions by type
Rather than arranging callback function in a haphazard fashion, grouping them by type makes them easier to find.
As we saw earlier all of your theme’s filter/action callbacks are registered in the class constructor, and all setup logic is placed within the init
method. I also like to group all my action callback functions together, my filter callback functions together and my utility methods together.
class YourThemeName {
function __construct() {
add_action('after_setup_theme', array($this, 'init'));
// Register action/filter callbacks here...
}
function init() {
// Theme setup code goes here...
}
// Action callbacks go here...
// Filter callbacks go here...
// Utility methods go here...
}
Comment your code
It might go without saying, but well written comments make your code easier to read and comprehend. Well structured comments can go a long way toward organizing your functions.php
file.
/**
* Get the category id from a category name
*
* @param string $cat_name The category name
* @return int The category ID
*/
function get_category_id ($cat_name) {
$term = get_term_by('name', $cat_name, 'category');
return $term->term_id;
}
Use a plugin for site-specific functionality
While it may be tempting to dump all your site’s custom functions into your functions.php
file, it is good practice to encapsulate site-specific logic in a separate functionality plugin. In addition to long term maintenance benefits, this separation of concerns streamlines your functions.php
file by offloading some of its functionality to a plugin.
There have been many good articles written about what belongs in a functions.php
file and what belongs in a functionality plugin, but I think Curtis McHale sums it up nicely:
The only deciding factor in where you put your code is the life of the functionality. Will you still need to have that portfolio custom post type the next time you redesign your site? If the answer is yes then you should be building a plugin with the code. If the answer is no, then put the code in you theme functions.php
.
Example functions.php file
If you employ the strategies I have outlined in this post I hope you will find that your functions.php
file is better organized and easier to manage. Here is an example of how the final file might look:
/**
* YourThemeName
*/
class YourThemeName {
/**
* Constructor
*/
function __construct() {
// Register action/filter callbacks
add_action('after_setup_theme', array($this, 'init'));
add_filter('excerpt_length', array($this, 'custom_excerpt_length'));
}
/**
* Theme setup
*/
function init() {
// Register navigation menus
register_nav_menus(
array(
'header-menu' => __('Header Menu', 'yourthemename')
)
);
}
/**
* Filter callbacks
* ----------------
*/
/**
* Customize post excerpt length
*
* @return int The new excerpt length in words
*/
function custom_excerpt_length () {
return 100;
}
/**
* Utility methods
* ---------------
*/
/**
* Get the category id from a category name
*
* @param string $cat_name The category name
* @return int The category ID
*/
function get_category_id ($cat_name) {
$term = get_term_by('name', $cat_name, 'category');
return $term->term_id;
}
}
// Instantiate theme
$yourthemename = new YourThemeName();