How To Properly Code Your Plugin For A WordPress Multisite

From the moment WordPress stopped the development of WordPress MU and moved the multi-site features to the core of the system, even more users started creating networks. Things are now much easier for users - updates and plugins can all be handled the same way regardless of your setup.

How To Properly Code Your Plugin For A WordPress Multisite
Image credit: php source code via shutterstock

How To Properly Code Your Plugin For A WordPress Multisite

What Should We, Plugin Developers, Do?

So, do you have to worry about WordPress networks when writing a plugin? I thought I didn't need to worry until one morning I got an angry mail from a customer who bought one of our premium plugins and couldn't get it to work. The errors which an improperly written or wrongly installed plugin can give on a network are quite mysterious. In fact you might be lucky if there are any visible errors at all!

So how can you avoid this nightmare? First, figure out how your multi-site users will want to use your plugin. There are two options and they lead to different development decisions:

  • Each blog in the network uses its own plugin data and does directly not interfere with the other blogs.
  • The plugin shares the data in all blogs in the network.

Let's explore the two options in detail.

Each Site In The Network Uses Its Own Plugin Data

I have to clarify what this means. For example I have a plugin for exams and quizzes. With this setup each site admin would create their own exams and publish them in their own blog, and will not be able to manage the tests of other admins. Or for example if you are creating a classifieds plugin, in this setup each admin would be able to publish or approve classifieds for their own blog, and not interfere with the classifieds on the other blogs.

This is the more common setup and it is how you should handle all your plugins unless you explicitly want different behavior. It ensures that the plugin works basically in the same way on single-site and multi-site installations (assuming it's properly activated - more on this below).

Here are the several important practices to follow when coding a plugin this way:

1. Use $wpdb->prefix
Always use $wpdb->prefix before your plugin table names. Some developers are confused about whether they should use $wpdb->base_prefix which also works perfectly well in single-blog installation. In networks however using $wpdb->base_prefix will lead to sharing data between the sites in the network.

I prefer to define constants for table names in my main plugin file so I don't have to use $wpdb->prefix everywhere. This is not a requirement of course but it would lead to cleaner code:

define( 'MYPLUGIN_CLASSIFIEDS', $wpdb->prefix. "myplugin_classifieds" );

If you choose to use defines you should properly call them on the plugins_loaded hook so you don't get "undefined constant" errors.

2. Use $wpdb Class Variables
Always reference the default WordPress database tables with the $wpdb class members instead of using $wpdb->prefix. WordPress offers you shortcuts for posts, comments, users, and more tables. So always use $wpdb->posts instead of $wpdb->prefix."posts". And do it not only because it's shorter. WordPress will make sure to use the appropriate when the plugin is used in a network of blogs. It will use the specific blog's posts table for posts, but it will reference the global users table for users! It's very easy to make it hard to catch bugs if you try to manage this on your own by using $wpdb->prefix and $wpdb->base_prefix.

Full reference of the available $wpdb class members is available here.

3. Use Absolute Paths
This advice is not specific for multi-site installations but is even more important for them. Never assume that each blog uses the default WordPress configuration and folder structure. Never assume it will have wp-content and plugins folders and that they will be in the default locations. So when including one plugin file in another, use absolute paths. The best way to handle this is to define them in your main plugin file:

define( 'MYPLUGIN_PATH', dirname( __FILE__ ) );

This way any further include or require function call will just use the constant:

require(MYPLUGIN_PATH."/controllers/myplugin.php");

When you need to output src tags for images or scripts, use plugins_url() function. When your plugin has to display clickable links, use site_url(). This way you will know your plugin URLs will be correct in any configuration, including heavily modified WordPress networks.

4. The Right Way To Install - Inform Your Users
The correct way to install a plugin in a multi-site setup is to install it as network admin, but activate it as blog admin. This way the plugin will get the proper DB prefix and will create tables for each blog. While at the same time the files will not need to be copied in every blog.

If you activate a plugin written this way as network admin it simply won't work correctly in the network blogs.

Sharing Plugin Data Between Sites

A less popular option is to write a plugin which is centrally managed in the WordPress network. Let's take our classifieds example again. The plugin can be written to allow only the network admin to manage the classifieds but all blogs in the network to display them.

Coding a plugin that shares data is easy and there's one main rule to follow:

Use $wpdb->base_prefix
Instead of using $wpdb->prefix as I strongly advocated above, this time you have to use $wpdb->base_prefix for your shared plugin tables. This will ensure that when running in a blog of the network the plugin still accesses the main tables. In this case it's also best to activate the blog as network admin.

Of course you may wish to keep some data different in each blog and even have tables that are shared and tables that are not. In this case use $wpdb->base_prefix for shared tables and $wpdb->prefix for blog specific tables. In which case you'd have to activate the blog as blog admin.

WordPress gives you the full flexibility to handle multi-site installations in every possible way. You just have to clearly understand how the different $wpdb variables work and who should activate your plugin.

The Plugin Settings
Usually you'll have to store some global settings in your plugin. Regardless of your setup type you'll have to use add_option(), update_option() and delete_option() functions for creating blog-specific settings. They will be stored in the local blog options table. On the other hand, if you want to set options at superadmin level, you should use add_site_option(), update_site_option() and delete_site_option(). These settings will be stored in the global wp_sitemeta table and will be valid for all blogs in the network.

Conclusion

If you follow the practices above your plugin should work fine on both single-site and multi-site installations using the same code base.