WordPress multisite has been aroung for quite some time now. But since many users still have a single-site installation and it’s not so easy to install WP Multisite using alternative ports (see this post), you will see that first not all plugins behave well in a multisite environment. Second, if you have a problem and google for it you will most likely find solutions working on single site installations but it’s not always to find out whether it will work in a multisite environment and if not, how to modify it to work in such an environment.
That’s the reason why I’ve started writing this post. First, I am a plugin author and need to have my plugins work in a multisite environment. Second, I am considering moving from multiple single sites to a multisite installation myself.
What’s WordPress Multisite?
WordPress Multisite was introduced in WordPress 3.0. It basically allows you to host multiple sites in a single WordPress installation. This means that once your WordPress installation is converted to multisite, it becomes a network of sites, with one central site used to administrate the network and a series of virtual sites building up your network.
The sites in this network are then not completely independent of another. They share some features. But they also have their own data and settings. So it is important to understand what’s shared and what not.
Just before we really start: in the WordPress multi-user solution which was available before Multisite was introduced in WordPress 3.0 the wording was a little bit different. That why I tend to sometimes mix it all. I’ll try to stick to the WP 3.0 wording but can’t promise I’ll always manage to do it.
What used to be called a Blog before WP 3.0 is now a site.
What used to be called a site before WP 3.0 is now a network.
What’s different with WordPress Multisite?
There are a few differences between a multisite based network of sites and a network of individual WordPress installations. Except for the WordPress PHP scripts themselves, a WordPress installation is basically made of some database content and the contents of the wp-content subdirectory.
wp-content
Let’s start with wp-content. It contains your plugins, themes and uploads (in three different directories).
Plugins
The plugins are installed for the whole network. Since you cannot install plugins per sites, there is no difference difference in the directory structure.
Note that this doesn’t mean that when you install a plugin it has to be active on all sites in the network. But it does mean that if a plugin is not installed globally, a site administrator cannot install this plugin just for a single site.
It also means that when a plugin is updated, it’s updated for all sites in the network. This is a good thing since you do not have to update each plugin for each site individually. On the other side, if you know that one site has a problem with the new version of a plugin and another site needs the new version, there is no way to handle this in a WordPress Multisite installation.
But even though the installation files are the same for all sites in the network give a specific plugin, this doesn’t mean that the settings of this plugins have to be the same for all sites. Depending on the plugin, you may want to have a site specific configuration or a network-wide configuration. See this post to learn more about network-wide plugin settings.
Also since the plugin files are available globally, this means that as a plugin developer, you do not need to take care of Multisite installations when enqueuing files (JavaScript or CSS files).
Themes
Themes work the same way as plugins in this case. Themes are installed globally and a user administrating a site in the network can activate these themes for his site. You can also restrict themes for individual sites and make them only available to a subset of the sites.
When working with themes and Multisite, it is important to keep in mind that whenever you update a file of the theme, you’re not modifying the file for one site but for all sites using this theme. So if you use a theme and would like to change a few things e.g. some CSS, instead of modifying the theme itself, you should do one of the following:
- Create child theme to use in the individual sites
- If it is already a child theme, you should create a copy of the theme before modifying it. The drawback is that you will not get automatic updates from wordpress.org anymore since it’s not the original theme anymore
- Use themes which provide you with means to make the adaptations you need in their settings
- If all you want to change are CSS styles, there are also plugins allowing you to load some additional styles
Uploads
The uploads work in a different way. Unlike the plugins and themes, upload can either be available on the network level or for a specific site.
So under the uploads directory, you will find one subfolder per year for the network-level uploads and a “sites” subdirectory. The “sites” subdirectory contains in turn one subdirectory per site. The names of this subdirectory are numbers representing the site ID. And in these sites subdirectories, you will again find a subdirectory per year.
The database
First of all, even in a Multisite installation, all tables, whether central tables or tables for individual sites in the network, are stored in a single MySQL database.
Assuming you chose the prefix “wp_” when installing WordPress, all central tables will be called wp_something and the site specific tables will be called wp_X_something, where X is the site ID.
The central site of your network will contain all tables you’d normally see in a single site WordPress installation. The individual sites in the network will only have a subset of these tables, containing the data which are stored per site:
- wp_X_comments and wp_X_commentmeta contain data related to comments
- wp_X_posts and wp_X_postmeta contain data related to posts
- wp_X_terms, wp_X_term_relationships and wp_X_term_taxonomy contain data related to terms and taxonomies
- wp_X_links contains site specific links
- wp_X_options contains sites specific settings and options
wp_X_options also includes data about plugins and themes available on individual sites.
As said before, the central site also has all these tables. Additionally, it also has a few more database tables:
- wp_blogs contains a list of all sites in the network. It’s still called “blogs” as in the old wording.
- wp_blog_versions contains the db version of each site. I guess you usually don’t care about this table.
- wp_users contains all users in the network
- wp_usermeta contains data related to the users in wp_users
- wp_registration_log contains a list of which users are registered on which site and when they registered
- wp_signups contains a list of users which signed up on you network along with the activation key and when/whether they were activated
- wp_site contains a list of networks and their primary domain. So you could have WordPress installation with multiple networks with each network having multiple sites
- wp_sitemeta contains additional information about the networks
Handling WordPress Multisite in your code
First of all, most WordPress functions you might be using are related to the current site. So when you get e.g. a list of categories using get_categories(), you will see only the categories for this one site. If you need to get e.g. the categories for all sites, it will require some extra work.
There are 5 functions which are relevant for such things:
- get_current_site()
- switch_to_blog()
- restore_current_blog()
- wp_get_sites()
get_current_site
This function returns a site object containing the following public variables:
- id: this is the site ID (number)
- domain: this is the domain name of the current site
- path: this is the URL path of the current site
- site_name: this is the name of the current site
You can also get the ID of the current site using get_current_blog_id().
switch_to_blog
This function takes a site ID as parameter. If the provided site ID doesn’t exist, it will return false.
Once you’re done doing whatever you need to do with this other site, you can get back to the original site using restore_current_blog().
restore_current_blog
You should call restore_current_blog() after every call to switch_to_blog(). The reason is that WordPress keeps track of site switches and if you do not use restore_current_blog, the global variable $GLOBALS[‘_wp_switched_stack’] will be left with some values in there.
An alternative is to clean up $GLOBALS[‘_wp_switched_stack’] yourself:
$GLOBALS['_wp_switched_stack'] = array();
$GLOBALS['switched'] = false;
wp_get_sites
This function returns an array with the list of all sites in the network. You can use this function to get a list of all sites and iterate through them (and switching from one blog to the next one using switch_to_blog()).
Iterating through sites
In order to iterate through sites e.g. in order to get all categories for all sites, you can do the following:
// store the original current site
$original_site = get_original_site();
// get all sites
$sites = wp_get_sites();
// loop through the sites
foreach ( $sites as $site ) {
// switch to this one site
switch_to_blog( $site['blog_id'] );
// do something here
}
// return to the original site
switch_to_blog( $original_site->id );
Note that by doing this, the global variables tracking the site switches will not be reset. So it is recommended to rather do:
// get all sites
$sites = wp_get_sites();
// loop through the sites
foreach ( $sites as $site ) {
// switch to this one site
switch_to_blog( $site['blog_id'] );
// do something here
// restore the original site
restore_current_blog();
}
Or something like this:
// store the original current site
$original_site = get_original_site();
// get all sites
$sites = wp_get_sites();
// loop through the sites
foreach ( $sites as $site ) {
// switch to this one site
switch_to_blog( $site['blog_id'] );
// do something here
}
// return to the original site
switch_to_blog( $original_site->id );
// manually reset the global variables tracking site switching
unset ( $GLOBALS['_wp_switched_stack'] );
$GLOBALS['switched'] = false;
 TO BE CONTINUED…