Quantcast
Viewing latest article 1
Browse Latest Browse All 10

Thoughts on Building Contemporary WordPress Plugins

I have been a passionate Laravel developer for the last few years, which has had a huge impact on how I approach coding now. Things such as object-oriented architecture, SOLID principles, test-driven and behavior-driven development, are now all part of my workflow, all of which seems light years ahead of the WordPress ecosystem. I’m not a WordPress expert, and the only part of the core that I have been looking into for now, is everything related to installing and updating packages. Honestly, it’s a true mess. The structure of the code is not contemporary if you look at other PHP open source projects (such as Laravel), and it makes it difficult for developers to write SOLID plugins that can take advantage of the core WordPress functionality. As an example, one call to the WordPress switch_theme() function in the core upgrader caused me a major headache. After downloading a theme from GitHub, I have to rename it. The only problem is that WordPress has already activated the theme, with the wrong name, meaning that I either had to overwrite the whole method where the call was made, or I had to call the same function again after the renaming. This problem illustrates how the lack of object-orientation in the WordPress core makes it hard for plugin developers to write prober SOLID code.

From the very beginning of the development, I decided that I wanted to use as many as my usual practices as possible, and as such didn’t do much research about what were current WordPress best practices. Because of that, you might find that some of my practices are stupid or ineffective. If so, please let me know. In the following I have highlighted a few ideas, if you will, as to how you can make your WordPress plugin code more contemporary.

PSR-4 autoloading of classes

Unless you want to clutter your whole codebase with include statements or, solving it the WordPress way, fill up your files with classes after classes, you should consider using autoloading. The PHP-FIG has a standard for autoloading named PSR-4, which is a great way of organising your files, classes and namespaces – one class per file and one namespace per directory.

As an example, the following class would be located in the file ‘plugin-dir/MyPlugin/Services/PluginUpgrader.php’:

namespace MyPlugin\Services;

class PluginUpgrader
{
    // ...
}

I’ve found several tutorials about how to implement autoloading for WordPress plugins. One of the recommendations was to use WordPress code standards for naming etc., which would somehow cause some problems with the PHP spl_autoload_register() function. I decided right away not to use WordPress conventions, because I really don’t like them. I used PSR-4 for organisation and used the example code provided by the PHP-FIG, which worked right away.

The following example code from the PHP-FIG works out of the box:

spl_autoload_register(function ($class) {

    // project-specific namespace prefix
    $prefix = 'MyPlugin\\';

    // base directory for the namespace prefix
    $base_dir = __DIR__ . '/MyPlugin/';

    // does the class use the namespace prefix?
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        // no, move to the next registered autoloader
        return;
    }

    // get the relative class name
    $relative_class = substr($class, $len);

    // replace the namespace prefix with the base directory, replace namespace
    // separators with directory separators in the relative class name, append
    // with .php
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';

    // if the file exists, require it
    if (file_exists($file)) {
        require $file;
    }
});

Put this code in a ‘autoload.php’ file and now the only require/include statement you need to write is:

require 'autoload.php';

And all your classes are available to you.

Extract your views

WordPress is a mess of PHP and HTML all cluttered together. We don’t like that.

Instead of squeezing all your HTML into your PHP files, why not extract a few view files?

Something simple like this makes it much more easy to manage your HTML:

add_action('admin_menu', 'myPluginMenu');

function myPluginMenu()
{
    add_options_page(
        'My Plugin Settings',
        'My Plugin',
        'manage_options',
        'my-plugin-admin',
        'myPluginAdminPage'
    );
}

function myPluginAdminPage()
{
    if ( ! current_user_can('manage_options')) {
        wp_die(__('You do not have sufficient permissions to access this page.'));
    }

    include 'views/admin_page.php';
}

Add your HTML forms or whatever to the ‘admin_page.php’ file and you have a clean separation of your logic and views.

Testing

As soon as you start separating concerns, using object-oriented practices and use something like PSR-4 autoloading, you can start using testing tools such as Behat and PhpSpec. If you use PhpSpec to spec classes that uses functions from the WordPress core, you’ll probably have to wrap these somehow, since they won’t be autoloaded. Maybe you can have some sort of WordPress wrapper or helper. This will also give you an overview over which WordPress functions you use (and depend on) in your plugin’s codebase.

Command-oriented architecture

Since the WP Pusher plugin works by providing a web service to the central dashboard, using a command-oriented architecture made a lot of sense to me. Basically, the plugin works by providing some defined commands, such as update plugin or install theme. In the base plugin file, I use a command translator class that will translate a command name to a class name. Then I will execute the command handler for this specific command. This gives me a very clean and testable code base that is easily extendable. If you are interested in this you can Google “command-oriented architecture” and “domain-driven design” and take a look at the WP Pusher source code, once it’s released.

This was just a few considerations I have had about building contemporary WordPress plugins. I might update this article once a while.


Viewing latest article 1
Browse Latest Browse All 10

Trending Articles