Tutorials UPDATED: 13 July 2023

Part 5 – WordPress and Object Oriented Programming: A WordPress Example – Implementation: The Administration Menu

Tassos Antoniou

12 min read

In our previous article on Object Oriented Programming we discussed the design we eventually came up with for our object-oriented plugin.

Now, we’ll get into the most exciting part where we’ll dig deeper into how we implemented it!

We’ll walk you through some parts of the implementation, one step at a time, talking about the very basics of object-oriented programming, PHP syntax, some core concepts, and we’ll even glance over the S.O.L.I.D. principles.

By the end of this article, you’ll hopefully have a better understanding of OOP and be excited about writing your own object-oriented plugins!

Getting Started

We assume you’re familiar with WordPress plugin development in general, so we’ll focus on the object-oriented aspects of our plugin. If you’re new to plugin development or need a refresher, you should learn how to build your first WordPress plugin first.

Let’s get started like we always do, by creating a new prsdm-limit-login-attempts.php file, under our plugin directory (i.e. /wp-content/plugins/prsdm-limit-login-attempts).

The main plugin file is going to include the Plugin Header you’re already familiar with:

/**
 * Plugin Name: PRSDM Limit Login Attempts
 * Plugin URI: https://pressidium.com
 * Description: Limit rate of login attempts, including by way of cookies, for each IP.
 * Author: Pressidium
 * Author URI: https://pressidium.com
 * Text Domain: prsdm-limit-login-attempts
 * License: GPL-2.0+
 * Version: 1.0.0
 */

And a simple if statement to prevent direct access to it.

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

That’s all we need for now. We’ll revisit this file later!

Building an Administration Menu

When you’re developing a plugin, you often need to provide your users with a way to configure it. That’s where a settings page comes in. To build one, we’re going to add an administration menu that utilizes the WordPress Settings API.

So, let’s start thinking about how our object-oriented API would look.

Ideally, we’d like to instantiate our Pressidium_LLA_Settings_Page and be done with it. To create an instance of a class, the new keyword must be used.

new Pressidium_LLA_Settings_Page();

Now, let’s think about how our Pressidium_LLA_Settings_Page class would look.

We’ll start by creating a new class, using the class keyword:

class Pressidium_LLA_Settings_Page {}

Our class name has to be prefixed with a unique identifier, Pressidium_LLA_ to prevent any naming collisions with other WordPress plugins. Prefixes prevent other plugins from overwriting and/or accidentally calling our classes. As long as our class names are unique—or we use namespaces—there won’t be any conflicts with other plugins.

The Constructor

Now, we’ll hook into admin_menu and admin_init. To keep things simple, we’ll just call add_action() in our constructor (spoiler alert: we’ll change this later).

class Pressidium_LLA_Settings_Page {

    /**
     * Settings_Page constructor.
     */
    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_page' ) );
        add_action( 'admin_init', array( $this, 'register_sections' ) );
    }

}

Classes which have a constructor, call this method when an object gets instantiated. So, the __construct() method is great for any initialization we might want to perform.

Let’s take a closer look at our add_action() calls. If you’ve developed WordPress plugins in the past, you might have expected something like this:

add_action( 'admin_menu', 'my_plugin_prefix_add_page' );

But instead, we’ve got:

add_action( 'admin_menu', array( $this, 'add_page' ) );

You might be confused about the use of an array here. Whenever we want to pass a method of an instantiated object as a callback/callable, we can use an array containing an object at index 0, and a method name at index 1.

What is $this?

It’s a pseudo-variable that is available when a method is called from within an object context. $this is the value of the calling object. In this case, $this is an instance of Pressidium_LLA_Settings_Page.

Plus, all of our “functions” are now methods, wrapped in a class, so there’s no need to prefix our method names.

Namespaces

Namespaces in PHP allow us to group related classes, interfaces, functions, etc., preventing naming collisions between our code, and internal PHP or third-party classes/functions.

Try our Award-Winning WordPress Hosting today!

Let’s go ahead and use them, so we don’t have to prefix any of our classes moving forward.

We’ll declare a namespace using the namespace keyword.

namespace Pressidium;

Namespaces can be defined with sub-levels.

namespace Pressidium\Limit_Login_Attempts;

Since we’re building a settings page, we’ll declare a “pages” sub-namespace to group anything related to administration pages together.

namespace Pressidium\Limit_Login_Attempts\Pages;

We can finally get rid of the Pressidium_LLA_ prefix!

namespace Pressidium\Limit_Login_Attempts\Pages;

class Settings_Page {
    // ...

Another WordPress plugin containing a Settings_Page class isn’t an issue anymore, since its class and our class won’t live in the same namespace.

When instantiating our Settings_Page within the same namespace we can omit it:

namespace Pressidium\Limit_Login_Attempts\Pages;

$settings_page = new Settings_Page();

When instantiating our Settings_Page outside of its namespace, we have to specify it like this:

namespace Another\Namespace;

$settings_page = new \Pressidium\Limit_Login_Attempts\Pages\Settings_Page();

Alternatively, we could import our class with the use operator:

use Pressidium\Limit_Login_Attempts\Pages\Settings_Page;
   
$settings_page = new Settings_Page();

Adding Hook Callbacks

Now, let’s declare these add_page() and register_sections() methods.

class Settings_Page {
   
    /**
     * Settings_Page constructor.
     */
    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_page' ) );
        add_action( 'admin_init', array( $this, 'register_sections' ) );
    }    

    /**
     * Add this page as a top-level menu page.
     */
    public function add_page() {
        // TODO: Implement this method.
    }

    /**
     * Register sections.
     */
    public function register_sections() {
        // TODO: Implement this method.
    }

}

Our add_page() method will just call the add_menu_page() WordPress function.

public function add_page() {
    add_menu_page(
        __( 'Limit Login Attempts Settings', 'prsdm-limit-login-attempts' ),
        __( 'Limit Login Attempts', 'prsdm-limit-login-attempts' ),
        'manage_options',
        'prsdm_limit_login_attempts_settings',
        array( $this, 'render' ),
        'dashicons-shield-alt',
        null
    );
}

That seems like a convoluted way to develop WordPress plugins. It’s simply calling WordPress functions, with extra steps.

Well, that’s not exactly “reusable”, we’d still have to write all this extra code for every administration menu/page we want to add.

Refactoring

Let’s go ahead and refactor our code a bit to take advantage of object-oriented programming and make our code reusable. We’ll start by replacing our hardcoded values in add_page() with a few methods, like so:

public function add_page() {
    add_menu_page(
        $this->get_page_title(),    // page_title
        $this->get_menu_title(),    // menu_title
        $this->get_capability(),    // capability
        $this->get_slug(),          // menu_slug
        array( $this, 'render' ),   // callback function
        $this->get_icon_url(),      // icon_url
        $this->get_position()       // position
    );
}

We’ll define these methods as protected, so they can be accessed only within the class itself and by its child/parent classes.

protected function get_page_title() { /* ... */ }
protected function get_menu_title() { /* ... */ }
protected function get_capability() { /* ... */ }
protected function get_slug() { /* ... */ }
protected function get_icon_url() { /* ... */ }
protected function get_position() { /* ... */ }

Great! We can now use this class as a reusable, generic class to extend from.

Redesigning

We told you this was probably going to happen eventually. Here we are, rethinking the design of our class while building it.

Since this is going to be our base class, we’ll rename it to a more generic name, like Admin_Page. So far, it looks like this:

class Admin_Page {

    /**
     * Admin_Page constructor.
     */
    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_page' ) );
        add_action( 'admin_init', array( $this, 'register_sections' ) );
    }    

    /**
     * Add this page as a top-level menu page.
     */
    public function add_page() {
        add_menu_page(
            $this->get_page_title(),    // page_title
            $this->get_menu_title(),    // menu_title
            $this->get_capability(),    // capability
            $this->get_slug(),          // menu_slug
            array( $this, 'render' ),   // callback function
            $this->get_icon_url(),      // icon_url
            $this->get_position()       // position
        );
    }

    /**
     * Register sections.
     */
    public function register_sections() {
        // TODO: Implement this method.
    }

    protected function get_page_title() { /* ... */ }
    protected function get_menu_title() { /* ... */ }
    protected function get_capability() { /* ... */ }
    protected function get_slug() { /* ... */ }
    protected function get_icon_url() { /* ... */ }
    protected function get_position() { /* ... */ }

}

We can now create a separate Settings_Page that extends that Admin_Page base class.

class Settings_Page extends Admin_Page {
    // ...
}

That’s a great example of inheritance, one of the core concepts of object-oriented programming. When extending a class, the child class—Settings_Page, in this case—inherits all of the public and protected methods, properties, and constants from the parent class.

We can make use of this and set some default values. For example, we’ll set a generic icon for all menu pages, by defining our get_icon_url() method like this:

class Admin_Page {

    // ...

    /**
     * Return the menu icon to be used for this menu.
     *
     * @link https://developer.wordpress.org/resource/dashicons/
     *
     * @return string
     */
    protected function get_icon_url() {
        return 'dashicons-admin-generic';
    }

}

Unless a class overrides those methods, they will retain their original functionality. So, by default, all child classes are going to use that generic icon.

However, if we want to set another icon for a specific menu page, we can simply override the get_icon_url() method in our child class, like so:

class Settings_Page extends Admin_Page {

    protected function get_icon_url() {
        return 'dashicons-shield-alt';
    }

}

There are some values, though, that must be different for each child class. For instance, the menu slug—the fourth argument of add_menu_page()—should be unique for each menu page.

If we’d define this method in our Admin_Page base class, we’d need a way to make sure that every single child class overrides this method. Well, we can do something even better. We can declare the method’s signature and completely skip its implementation. 

Enter abstract methods!

Abstract Classes and Methods

Methods defined as abstract simply declare the method’s signature and they cannot define its implementation.

/**
 * Return page slug.
 *
 * @return string
 */
abstract protected function get_slug();

Any class that contains at least one abstract method must also be abstract. That means, our Admin_Page class should be defined as abstract as well.

abstract class Admin_Page {
    // ...

It’s also important to point out here that classes defined as abstract cannot be instantiated. So, we can no longer directly instantiate Admin_Page.

Here’s also a visualization of the class:

When inheriting from an abstract class, the child class must define all methods marked abstract in the declaration of its parent class. Meaning, that our Settings_Page has to implement the get_slug() method.

class Settings_Page extends Admin_Page {
   
    // ...

    protected function get_slug() {
        return 'prsdm_limit_login_attempts_settings';
    }
   
    // ...

}

In the same way, we should implement the rest of the protected methods the add_page() needs.

Before proceeding on how we’ll register the sections and fields of the admin page and render their content, let’s talk a bit about settings in WordPress.

The Settings API

We’ll assume you’re already familiar with the Settings API. But, just in case, here’s the gist of it:

If you are not already familiar with this, you can pause reading this article and check our related article on how to build the settings page for a custom plugin.

Now that we’re on the same page, let’s get back to our register_sections() method. Once again, we have to take a step back and think about our API.

Since we’ve defined the add_page() method in the Admin_Page class, we’ll also define the render() method there as well. We’ll pass the return values of our other methods as arguments to the WordPress functions.

abstract class Admin_Page {

    // ...

    /**
     * Render this admin page.
     */
    public function render() {
        ?>

        <div class="wrap">
            <form action="options.php" method="post">
                <h1><?php echo esc_html( $this->get_page_title() ); ?></h1>
                <?php
                settings_fields( $this->get_slug() );
                do_settings_sections( $this->get_slug() );
                submit_button( __( 'Change Options', 'prsdm-limit-login-attempts' ) );
                ?>
            </form>
        </div>

        <?php
    }

}

That way, we won’t have to bother directly with these WordPress functions ever again. That’s because any admin page we may add in the future will be built through a child class just like the Settings_Page, and its rendering will be done through the inherited render() method of the Admin_Page parent class.

Conclusion

Great! We created the classes that are responsible for registering an administration menu and adding a settings page.

Next, in Part 6 in our Objected Oriented Programming Series, we’ll keep building our settings page and register its sections, fields, and elements.

See Also

Start Your 14 Day Free Trial

Try our award winning WordPress Hosting!

OUR READERS ALSO VIEWED:

See how Pressidium can help you scale
your business with ease.