Simplifying WordPress's functions.php with OOP
Tags: wordpress, oop, php
Published: 2018-11-30 17:05:17

---
title: Simplifying WordPress's functions.php with OOP
published: true
description: Using object-oriented programming to clean up spaghetti code in functions.php.
tags: WordPress, OOP, PHP
cover_image: https://thepracticaldev.s3.amazonaws.com/i/okeklx72uayezqo7kedh.jpg
---

I love WordPress, and I use it for most of my clients' sites. Between its built-in features, rich plugin ecosystem and endless learning resources/documentation, I can spend more time building features unique to each project and less time reinventing the wheel.  

That being said, WordPress isn't perfect. Like PHP itself, the WordPress API often feels clunky and inconsistent. I spend lots of time Googling functions and action hooks that I frequently use because I can't remember their names. This part of WordPress is less than ideal.

When I learned Laravel, I discovered how much better coding could *feel* when using a simple and elegant object-oriented API. That feeling was like a drug: once I had a taste I was hooked. I wanted to lace my WordPress development with a little bit of that sweet object-oriented programming (OOP for short).

Enter `functions.php`.

I maintain my own [Underscores](https://underscores.me/)-based starter theme that I use for most of my projects, and its `functions.php` felt clunky. My functions file was fairly typical and close to the stock [Underscores functions.php](https://github.com/Automattic/_s/blob/master/functions.php). Here's a rundown of what it was doing:

1. Inside the `after_setup_theme` hook:
  * **Add** theme support for `title-tag`, `custom-logo`, `post-thumbnails`, `customize-selective-refresh-widgets` and an array of `html5` components using `add_theme_support()`.
  * **Register** navigation menus using `register_nav_menus()`.
  * **Add** image size using `add_image_size()`.
2. Inside the `wp_enqueue_scripts` hook:
  * **Enqueue** styles and scripts with their respective `wp_enqueue_style()` and `wp_enqueue_script()` functions.
3. Include related files.

Again, this is a fairly typical `functions.php`, and there's nothing wrong with it. However, I have a few issues with how it's setup:
 
1. Memorization isn't my biggest strength, and remembering which functions start with the word **add**, **register** and **wp_enqueue** just isn't going to happen for me.
2. Action hooks fail silently, and I can't tell you how many times I've typed `after_theme_setup` instead of `after_setup_theme`.
3. I literally add theme support for the exact same things in every project, and I don't really want that identical boiler plate cluttering my `functions.php` code.

Let's take a step back and consider what the `functions.php` code is *actually* doing here. 

When you think about it, everything that's happening is performing some kind of action on the theme itself. What if we had a **theme object** that we could perform these actions on with a simple, object-oriented API?

##A Simple, Object-Oriented API

I don't want to remember which functions start with the word **add**, **register** or **wp_enqueue**. In fact, all of these essentially do the same thing: they **add something** to the theme. So I'm going to use the word **add** for all of these.  I want to **add** theme support, **add** nav menus, **add** image sizes, and **add** scripts. 

I'm lazy. Fight me.

I want my `functions.php` to look more or less like this:

```php
<?php // functions.php

require get_template_directory() . '/classes/theme-class.php';

$theme = new MyTheme;

$theme->addNavMenus([
    'menu-1' => 'Primary',
]);

$theme->addSupport('post-thumbnails');

$theme->addImageSize('full-width', 1600);

$theme->addStyle('theme-styles',  get_stylesheet_uri())
      ->addScript('theme-script', get_template_directory_uri() . '/js/custom.js');

```

No more memorizing hook names and function prefixes. Rejoice! 

It is also abundantly clear that all of these methods are performing an action on the theme itself.

So let's build this.

We'll start by defining a theme class in a new file and building a `addNavMenus()` method. Essentially, we're just building wrappers around the existing WordPress hooks and functions, so this shouldn't be too complicated. 


```php
<?php // theme-class.php

class MyTheme
{
    public function addNavMenus($locations = array())
    {
        add_action('after_setup_theme',function() use ($locations){
            register_nav_menus($locations);
        });
    }
}

```

Let's unpack what's going on here. 

We define our class `MyTheme`, make a public method for `addNavMenus()` and give it the same arguments as the `register_nav_menus()` WordPress function. 

Inside the method, we add an action to the `after_setup_theme` hook, and create a closure (PHP's flavor of an anonymous function) where we call the WordPress `register_nav_menu()` function. The `$locations` variable is passed into the closure using PHP's `use` keyword, otherwise the variable would be outside of the closure's scope.

*Side note: closures are supported as of PHP 5.3, and they are how I interact with WordPress hooks 90% of the time to avoid cluttering up the global namespace. However, despite WordPress's adoption of modern technologies like React.js, WordPress officially maintains its PHP backwards compatibility to PHP 5.2, which reached its end-of-life in January 2011* 🤷‍♂️

##Reduce Hook Failures

In the `addNavMenus()` method, we've solved the first problem we defined above: we've simplified the API (no more remembering prefixes like **register**). We still have our second problem though: misspelled hooks fail silently. As I build out my theme class's methods, at some point I'm probably going to write `after_theme_setup` instead of `after_setup_theme` somewhere and not notice. 

Let's fix that by creating a private method that fires the `after_setup_theme` action hook, then calling that method within the `addNavMenus()` method instead of the `add_action()` function.

```php
<?php // theme-class.php

class MyTheme
{
    private function actionAfterSetup($function)
    {
        add_action('after_setup_theme', function() use ($function) {
            $function();
        });
    }

    public function addNavMenus($locations = array())
    {
        $this->actionAfterSetup(function() use ($locations){
            register_nav_menus($locations);
        });
    }
}

```

So this is kind of cool: we're passing in the closure within `addNavMenus()` to our `actionAfterSetup()` method, then passing it to the closure via the `use` keyword, then calling the code from its variable name from within the closure. Wonderful witchcraft! 

...if that description didn't make sense at all, just study the code and it isn't too bad.

I've prefixed the method with the word "action" to tell me this is an action hook, and I've made it private because this should only be used within the class.

This solves our second problem: if we type the `actionAfterSetup()` method incorrectly, it will no longer fail silently. Now we only need the hook name to be correct in one place. 

Let's add some more methods! 

##Abstracting Code Shared Between Projects

I add theme support for the same features on almost every project: `title-tag`, `custom-logo`, `post-thumbnails`, `customize-selective-refresh-widgets` and an array of `html5` components. 

Let's add a wrapper for the `add_theme_support()` function. We'll simply call it `addSupport()` as including the word "theme" feels redundant on the theme class. Once implemented, we'll see how we can abstract some of the repeated code.

The code that powers the `add_theme_support()` function is kind of wonky: it counts the number of arguments passed into it to determine what it should do. Because of this, we're going to set up a conditional within our wrapper to see if a second argument is set and only pass in the the second argument if it has a value.

```php
<?php // theme-class.php

class MyTheme
{
    /** Previous code truncated for clarity */

    public function addSupport($feature, $options = null)
    {
        $this->actionAfterSetup(function() use ($feature, $options) {
            if ($options){
                add_theme_support($feature, $options);
            } else {
                add_theme_support($feature);
            }
        });
    }
}

```

Additionally, I'd like to be able to chain several of these methods together, so I'm going to have `addSupport()` and all other public methods return `$this`. 

```php
<?php // theme-class.php

class MyTheme
{
    /** Previous code truncated for clarity */

    public function addSupport($feature, $options = null)
    {
        $this->actionAfterSetup(function() use ($feature, $options) {
            if ($options){
                add_theme_support($feature, $options);
            } else {
                add_theme_support($feature);
            }
        });
        return $this;
    }
}

```

Now that we have implemented a way to add theme support, let's abstract away the settings we're going to want on every project by creating a usable set of defaults for the theme. We can do this by using the class constructor.

```php
<?php // theme-class.php

class MyTheme
{
    /** Previous code truncated for clarity */

    public function __construct()
    {
        $this->addSupport('title-tag')
             ->addSupport('custom-logo')
             ->addSupport('post-thumbnails')
             ->addSupport('customize-selective-refresh-widgets')
             ->addSupport('html5', [
                 'search-form',
                 'comment-form',
                 'comment-list',
                 'gallery',
                 'caption'
             ]);
    }
}

```

Because these are now in the class constructor, this code will execute immediately once a theme object is instantiated, meaning we no longer have to clutter the functions file with boiler plate that is used in every site we build with this theme. 

Instead, the functions file only defines the parts of the theme that are different across each project.

##Going Further

This is just the beginning, but there's much more to be done! You can create wrappers to add scripts and stylesheets. You can add `style.css` and `main.js` to your theme automatically through the theme class constructor, streamlining `functions.php` even further. You can create methods to remove theme support and remove styles/scripts. This approach is very flexible and ultimately leads to less spaghetti code.

If you want to see where I landed with my own theme class, look at my [GitHub gist](https://gist.github.com/tylerlwsmith/dc1f5fb04126189ec42980952cd124d7). 

I hope this was helpful and showed you some nifty object-oriented options for WordPress. If you're interested in learning more about object-oriented programming for WordPress, I strongly recommend checking out the [Object-Oriented Theme Development WordCamp Talk](https://wordpress.tv/2015/01/14/kevin-fodness-object-oriented-theme-development/) by Kevin Fodness. For those who are already using OOP in WordPress, I've love to hear your opinions about camelCase vs snake_case for method names, PHP namespaces and more. Cheers!