Creating a site-specific WordPress plugin

Site-specific plugins (or “site plugins”) are a common part of professional WordPress projects. They are useful for adding functionality to a site without strictly coupling that functionality to a theme’s functions.php file.

Site plugins are ideal for implementing custom post types, taxonomies, meta boxes, shortcodes, and other functionality that should be preserved even if an admin changes the theme. They are also great for specifying tweaks and security enhancements such as disabling XML-RPC, a rarely-used feature that provides a vector for attack.

Plugin basics

A WordPress plugin can be as simple as a single PHP file that begins with a Plugin Header: a specially-formatted comment that WordPress uses to recognize it as a plugin.

Only a “Plugin Name” is required in the Plugin Header, however the example below includes a number of recommended fields:

<?php
/*
Plugin Name: Site plugin for firxworx.example.com
Plugin URI: https://developer-website.example.com/unique-plugin-url
Description: Implements custom post types (or whatever) for firxworx.example.com
Version: 1.0
Author: your_name
Author URI: https://author.example.com 
*/

If you intend to support internationalization, specify your text domain (e.g. “Text Domain: example”) in the Plugin Header as well. It might also be important to specify a “License” and “License URI” for your project.

After the Plugin Header, you can define any functions and hooks as you normally would in a theme’s functions.php file.

Site Plugins as Must-Use Plugins

A Must-Use plugin (or mu-plugin) is a special type of plugin that will not appear on the Plugins page in wp-admin and can only be deactivated by deleting their associated file(s).

On client projects where stakeholders are given full admin access, it can be helpful to deploy a site plugin as an mu-plugin to prevent accidents.

Mu-plugin requirements

Any plugin that does not depend on Activation or Deactivation hooks can be deployed as an mu-plugin. Since an mu-plugin is “always there” these concepts do not apply.

An example of a plugin that depends on Activation/Deactivation hooks and therefore can’t be an mu-plugin is one that needs to create (on activation) and remove (on deactivation) database tables for its functionality.

Updating mu-plugins

Update notifications are not available for mu-plugins and updates to them must be performed manually. This is usually the case anyway for custom client work, however this fact can also be useful for projects that incorporate 3rd-party plugins as part of their overall solution.

Since mu-plugins are effectively “locked” to their current version there is no chance an admin can deploy a major update that could risk breaking compatibility with the rest of their site. Developers can take the opportunity to test new versions before they apply them manually.

The practice of “locking down” distributed plugins should only be used in scenarios where the developer is actively supporting the project and ensuring pending updates are regularly applied. Otherwise it may be a wiser security and stability choice to stick with traditional plugin deployments.

Deploying a plugin as a must-use plugin

WordPress looks for mu-plugins in the wp-content/mu-plugins folder by default. This path can be customized by defining the WPMU_PLUGIN_DIR and WPMU_PLUGIN_URL constants in wp-config.php.

Unlike traditionally-deployed plugins, mu-plugins must be PHP files that exist in the root of the wp-content/mu-plugins/ folder. A more complex plugin can be deployed as an mu-plugin by creating a “loader” script to serve as the required PHP file and then using it to pull in the rest of the plugin’s dependencies.

Mu-plugins are loaded in alphabetical order before any “normal” plugins in the wp-content/plugins folder.