Organize your WordPress functions.php file
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();
4 thoughts on “Organize your WordPress functions.php file”
Comments are closed.
Hi Jonathan,
Just wondering how i would implement the WordPress Theme Logo function into the above?
Im not sure where i should place the code or how to call it from my template file.
https://codex.wordpress.org/Theme_Logo
Hi Jonathan,
Great post and example of how to organize the functions.php file. I agree this approach seems tidy and easy to maintain. I also read your post about a super minimal functions.php file. I think I lean toward the minimal approach but I’m still deciding which I prefer. :)
Q: By adding action callbacks, filter callbacks and utility methods to a single class, I could see how the functions.php file could still become quite large. Would you recommend creating a separate class for actions, a separate class for filters, and so on?
Q: When passing a class method as a callback, is there an advantage to using a static class method versus an instance method?
Thanks for your informative post!
@Josh Hi Josh, tthe usefullness of classes and commenting code are explained very clearly in this article. What the author forgot to mention, is that to really clean up your functions.php, you should put your classes into separate php files. Then only an include(class-filename.php) is sufficient to make your class be implemented. Do not forget to make an instance of your class in your include file, but do that outside your class definition.
@Josh – Sorry, I overlooked your comment. I actually did a follow up article to this one that shows a different approach where logic is split into multiple includes.
Since writing both these articles I have adopted a different approach using namespacing that I think gives the best of both worlds: Namespacing to avoid the possibility of naming conflicts with WordPress’ or plugin authors global functions, and separate includes for tidiness. Perhaps I will describe that approach in another post.