Hacking Multisite WordPress’ Domain Mapping

I had been using the old WordPress MU Domain Mapping plugin for multisite WordPress, and was pretty happy with it, but unknown to me (because I only started reading WP blogs and groups this past year) WordPress has rolled this feature into the core.

The old plugin still works, but now you don’t need it. To uninstall it, delete wp-content/sunrise.php, the tables the plugin created, and the plugin directory. Manually re-map the domains by setting the value of the URL.

I am working on a hack to create a mapping of pages onto subdomains. (This naturally clashes with multisite, which also uses subdomains, so it needs to selectively perform its mapping only on specified domains.)

To implement this, I needed to learn about the way multisite blogs are loaded. The sequence is:

index.php
wp-load.php
wp-config.php
wp-settings.php
ms-settings.php
sunrise.php

Libraries being loaded are not listed here. It’s just the important files that are run during the execution.

Mapping domains and paths to WordPress blogs and networks happens in code called from ms-settings.php.

By the time we reach ms-settings.php, the plugins have not been loaded. So, we cannot implement the feature with a plugin. It must be implemented in sunrise.php, a file that, if enabled, is loaded by ms-settings specifically to allow us to alter how the system will discover the current blog and current network of sites.

(Just to note, WP supports multiple networks of sites. I’m not sure of how this works, because it’s not written about much, but it’s in there. You can have only one one network with many blogs – you can also have many networks, each with many blogs. It appears that to have many networks, the blogs need to be path-based, not subdomain-based.)

So, create sunrise.php, then enable it by adding this line to wp-config.php

define('SUNRISE', 1);

In the sunrise.php script, we could do our domain mangling, and set the globals $current_site and $current_blog. (Note again, in some parts of the WP code, “site” means “network” in the admin pages, and “blog” means “site” in the admin pages. So $current_site is the current network, and $current_blog is the current site. This is a totally annoying naming clash, so I’m going to stick with “network” and “blog”, avoiding the use of “site” entirely, unless we’re talking about WP_Site and WP_Network.)

We could do that, but there’s already existing code that will do that for us, taking care of a lot of extra stuff. Instead of duplicating all that, can hook into a filter inside ms-load.php, in the get_site_by_path() function.

get_site_by_path($domain, $path, $segments) takes the domain and path, and spits out a WP_Site object.

Internally, it calls a filter named ‘pre_get_site_by_path’ that can short-circuit the process, allowing us to override the default search method.

Even better, if we only want to affect specific domains, not all domains, we can just return FALSE, and the default behavior applies.

Likewise, the function WP_Network::get_network_by_path() has a filter, ‘pre_get_network_by_path’, that performs a similar role with matching networks. This is called only if there is more than one network configured.

The following function doesn’t do anything except dump its arguments, but that’s where I am.

<?php

namespace JK\DMPM;

function mangle($ignore, $domain, $path, $segments, $paths ) {
    echo var_dump($ignore, $domain, $path, $segments, $paths);
    exit();

    $site = \WP_Site::get_instance(3);
    return $site;
}
add_filter('pre_get_site_by_path', 'JK\DMPM\mangle', 10, 5); 
add_filter('pre_get_network_by_path', 'JK\DMPM\mangle', 10, 5); 

I was just so jazzed to find the right way to do this, that I had to share.