Developing a Minimum Viable WordPress Plugin

What is the minimum amount of coding required to create a new plugin that shows up in the WordPress dashboard? How much code has to be written to get the plugin to do something minimally interesting?

It’s really pretty easy to create a minimal WordPress plugin to experiment with. Simply create a .php file under the “wp-content/plugins” folder with a comment in the header that has some metadata in it that names and describes the plugin. I created a file named “mv-oo-plugin.php” with the following content:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
/*
Simple experiment in creating a WordPress plugin
 
@wordpress-plugin
Plugin Name:  Minimum Viable Plugin (Object Oriented)
Description:  An experiment in creating a very simple plugin.
Author:       MV Developer
Version:      0.0.1
*/
 
function_exists('add_action') or die('Nothing to see here, move along.');

With this simple setup, the plugin will show up as installed but inactive in the plugins list on the dashboard, as well as in the wp-cli interface, using the name of the PHP file as the name of the plugin:

1
2
3
4
5
6
7
8
9
10
$ wp-cli plugin list
+---------------------------+----------+-----------+----------+
| name                      | status   | update    | version  |
+---------------------------+----------+-----------+----------+
| akismet                   | inactive | available | 3.3.4    |
| error-log-monitor         | active   | none      | 1.5.6    |
| hello                     | inactive | none      | 1.6      |
| mv-oo-plugin              | inactive | none      | 0.0.1    || wp-syntax                 | active   | none      | 1.1      |
+---------------------------+----------+-----------+----------+

The WordPress admin dashboard shows the information from the header of the php file: Plugin name, description, version, and author.

If a plugin file is created like this but doesn’t show in the dashboard or CLI, it may be that the hosting service sets up the web server to run under a different username than the owner account. In this case making the php file readable by “other” users should make it visible in the dashboard:

1
$ chmod o+r mv-oo-plugin.php

Implementing Real Functionality in the Plugin

Enough has been done to make the plugin show up in the dashboard, but it doesn’t really do anything yet. The only code in it so far is the ‘function_exists(‘add_action’)’ statement, a security measure to prevent the plugin from being executed outside of the context of the WordPress plugin framework.

For a real plugin, you’d be considering how to do something that appears in WordPress, maybe a widget that displays some useful information, either in the user-visible WordPress pages or in the admin dashboard. Or perhaps you’d implement a “short code” that inserts text or modifies existing text in a post or comment.

Just to get something running I added some code that uses error logging (the WordPress ‘error_log()’ function) to track some plugin lifecycle events and also to log when requests arrive at the WordPress site:

<?php
/**
Simple experiment in creating a WordPress plugin
 
@wordpress-plugin
Plugin Name:  Minimum Viable Plugin (Object Oriented)
Description:  An experiment in creating a very simple plugin.
Author:       MV Developer
Version:      0.0.1
*/
 
function_exists('add_action') or die('Nothing to see here, move along.');
define('MV_PLUGIN_VERSION', '0.0.1');
 
class MVPlugin {
    public static function setup() {
        register_activation_hook( __FILE__, 'MVPlugin::activate' );
        register_deactivation_hook( __FILE__, 'MVPlugin::deactivate' );
        register_activation_hook( __FILE__, 'MVPlugin::install' );
        register_deactivation_hook( __FILE__, 'MVPlugin::uninstall' );
        add_action('init', 'MVPlugin::init');
        add_action('parse_request', 'MVPlugin::parse_request');
    }
 
    /* Action functions */
    public static function init() { MVPlugin::log('Init...'); }
 
    public static function parse_request() {
        $ipaddr = $_SERVER['REMOTE_ADDR'];
        $method = $_SERVER['REQUEST_METHOD'];
        $request_path = $_SERVER['SCRIPT_NAME'];
        MVPlugin::log('... ' . $ipaddr . ' ' . $method . ' ' . $request_path);
    }
 
    /* Some plugin lifecycle functions - just to see that they execute */
    public static function activate() { MVPlugin::log("Activate..."); }
    public static function deactivate() { MVPlugin::log("Deactivate..."); }
    public static function install() { MVPlugin::log("Install..."); }
    public static function uninstall() { MVPlugin::log("Uninstall..."); }
 
    private static function log($msg, $caller='') {
        if ('' == $caller) {
            $trace=debug_backtrace();
            $caller = ( '' == $caller ) ? $trace[1]['function'] : $caller;
        }    
        // $msg = print_r( $msg, true );
        $log = $caller . '() ' . $msg;
        error_log( $log );
    }
}
 
MVPlugin::setup();

You’ll probably want to use error_log() to help troubleshoot your plugin as you develop it anyway, so this is a good start at some useful code.

Plugin programming is all event driven: code starts with responding to some event in a page of the blog. The core of the code for this example plugin is the setup() function, which uses register_activation_hook() and register_deactivation_hook() to set some functions that track the “lifecycle” of the plugin, and some add_action() calls to execute some code when a page in the blog site is initialized and begins to process the browser request for the page content. In addition to setting activation and deactivation hooks as well as intercepting actions with add_action() bindings, there is also a add_filter() call that can be used to register functions that modifies the content returned for a page in some useful way.

The above code uses an object oriented style of development. This is mainly to namespace features (functions and variables) in the code. All methods are declared as static so that they can be referenced by the class name, like “MVPlugin::activate” (“ClassName::method_name”). This way the method names (the setup() method in particular) won’t conflict with any other function in WordPress (or installed themes or plugins), and the log() method – having been declared private – won’t be visible outside the MVPlugin class at all. At very least, WordPress recommends choosing a standard prefix for all functions and variables used in a plugin, something that will be unique to the plugin (perhaps “mvp_” in this case?), but a class declaration provides control over visibility as well as naming.

Some configuration has to be added to the bottom of the “wp-config.php” to say where logged messages get written:

ini_set('log_errors', 'On');
ini_set('error_log', '/usr/home/mvdev/php_logs/php_error.log');
error_reporting(E_ALL);

With this additional code added and the php_error.log file configured in wp-config.php, I can click “Activate” on entry for the plugin in the dashboard; the following output should be logged to php_error.log:

[06-Dec-2017 14:14:29 UTC] activate() Activate...
[06-Dec-2017 14:14:29 UTC] install() Install...
[06-Dec-2017 14:14:29 UTC] init() Init...

Now browse to the blog; the plugin should initialize as part of processing the display request, and the request for “/index.php” should be logged too:

[06-Dec-2017 14:14:57 UTC] init() Init...
[06-Dec-2017 14:14:57 UTC] parse_request() ... 192.241.248.13 GET /index.php

Clicking “Deactivate” on the Minimal Viable Plugin in the plugins window will result in log messages like this:

[06-Dec-2017 14:22:38 UTC] init() Init...
[06-Dec-2017 14:22:38 UTC] deactivate() Deactivate...
[06-Dec-2017 14:22:38 UTC] uninstall() Uninstall...

Editing the Plugin in the WordPress Dashboard

The plugin .php file has to be readable in order for it to run, but a great extra feature gets activated if you make the .php file writeable:

1
$ chmod uo+w mv-oo-plugin.php

With this, the admin dashboard shows an “edit” link in the list entry for the plugin; clicking this link leads to a page with a text field that contains the source code for the plugin, allowing it to be edited.

Another great feature of the plugin edit panel is the Documentation menu. The menu displays a list of WordPress API functions that are being used in the plugin code. Select a function from the menu and click “Lookup”; a separate tab opens with the documentation for that function.

The edit panel provides a convenient way to interactively develop the plugin in the dashboard. But pay attention to the message presented at bottom of this panel:

“Warning: Making changes to active plugins is not recommended. If your
changes cause a fatal error, the plugin will be automatically
deactivated.”

In addition to this, if you make a mistake in the syntax of the PHP code, this might disable the entire site (creating the WordPress “White Screen of Death”), or at very least causing an error message to be displayed at top of every page in the blog! A relatively simple PHP syntax error would be enough to do this. In practice, the best way to avoid this kind of problem would be to create a separate “developer” install of WordPress, either on your local computer or perhaps in a separate subdomain of your WordPress site (many hosting plans allow multiple subdomains to be defined). With a separate install you can develop the plugin in peace, making all the mistakes you have to in order to get it right, and then copy your work over to the public site.

Moving on to More Substantial Development

A larger plugin development project will have to have multiple files to make it manageable: multiple .php files, maybe HTML, javascript, and CSS files. For real work, all this plugin code is put in a folder under the “wp-content/plugins” folder rather than placing a simple .php file there.

The folder must be readable and searchable (‘chmod o+rx mv-oo-plugin’).

It’s also good practice to create an index.html (it can be an empty file with that name) so that someone browsing from outside can’t get a directory listing of the plugin files.

If you want to offer your plugin in the WordPress plugin store, you’ll have to create a “readme.txt” file in the top level folder of the plugin. You’ll also have to put the code for your plugin in a Subversion project that is created by visiting the WordPress site. See the WordPress documentation for more details on how this is done.

TL; DR

It’s easy to experiment with coding a WordPress plugin:

  • Create a php file under the “wp-content/plugins” folder
  • Put metadata in the header comment of the .php file to identify it as a WordPress plugin: “@wordpress-plugin” and some properties describing the plugin
  • Make sure the php file is readable by other (if your server requires it)
  • Manage in the WordPress admin dashboard or with the wp-cli

If the php file is writeable, an edit link is available in the dashboard, which allows the plugin code to be edited and includes documentation assist on WordPress related functions.

Create a folder for the plugin for more substantial work, putting the php file (along with other files) in it.

References

Header Requirements: Plugin Developer Handbook, WordPress Developer Resources

Documentation on the error_log() function.

Discussion of debugging a WordPress plugin, including the inspiration for the code used in the log() above for finding the caller method when the error_log() function is called: How do I debug a WordPress plugin? – Stack Overflow

How Your Readme.txt Works | Plugin Developer Handbook | WordPress Developer Resources

How to Use Subversion | Plugin Developer Handbook | WordPress Developer Resources

Versions

WordPress 4.8.4

Add a Comment