One way to achieve this, is to alter the way WordPress maps the request to a blog in your multisite network by using the sunrise.php
drop-in. Sunrise overwrite the default setup in wp-includes/ms-settings.php
. There, a bunch of global variables are set up like $current_blog
or $current_site
.
To activate this dropin you have to define the constant SUNRISE
in your wp-config.php:
define( 'SUNRISE', TRUE );
Now, WordPress looks up for a sunrise.php
file in your WP_CONTENT_DIR
directory. (By default it is /wp-content
).
In this sunrise.php you have to do at least the following things:
- Parse the request to get the domain and the path
- Setup the the global
$current_site
using the functionget_current_site()
- Looking for your blog in the
wp_blogs
database table using your domain and path. - With this data, set up the global
$current_blog
Here’s an example of how i played with this. It’s not perfect but might get you an idea of how this could look like:
(I use namespaces so be aware of using at least PHP 5.3 or striping the namespace references.)
The shown example does not limit the path segments to two levels like you want but matches the complete path against the database, so that even one or more than two levels are possible as base for a own blog.
namespace dna\Blog_Mapping;
sunrise();
/**
* init the globals $current_blog and $current_site
*
* @global $current_blog
* @global $current_site
* @global $wpdb
* @global $path
* @global $cookie_domain
* @return void
*/
function sunrise() {
if ( defined( 'WP_INSTALLING' ) )
return; # don't touch the installing process
if ( ! \is_subdomain_install() )
return;
/**
* strip port numbers in the host
*/
$domain = addslashes( $_SERVER['HTTP_HOST'] );
if ( false !== strpos( $domain, ':' ) ) {
if ( substr( $domain, -3 ) == ':80' ) {
$domain = substr( $domain, 0, -3 );
$_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -3 );
} elseif ( substr( $domain, -4 ) == ':443' ) {
$domain = substr( $domain, 0, -4 );
$_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -4 );
} else {
\wp_load_translations_early();
\wp_die( __( 'Multisite only works without the port number in the URL.' ) );
}
}
$current_site = \get_current_site();
# setup the sitename to the current_site object
$current_site = $current_site->site_name;
# find the blog by domain and path
$uri = \parse_url( $_SERVER[ 'REQUEST_URI' ] );
$path = \preg_replace( '|([a-z0-9-]+.php.*)|', '', $uri[ 'path' ] );
$path = \str_replace ( '/wp-admin/', "https://wordpress.stackexchange.com/", $path );
$current_blog = get_blog_details( $domain, $path );
# at this point, we are obviously at the root blog
# using the core function to get blog by root blog id
if ( ! $current_blog ) {
$current_blog = \get_blog_details( $current_site->blog_id );
}
if ( empty( $current_blog->site_id ) ) {
$current_blog->site_id = ( isset( $current_site->id ) )
? $current_site->id
: 1;
}
$blogname = substr( $domain, 0, strpos( $domain, '.' ) );
$blogname .= $current_blog->path;
# set globals like in wp-includes/ms-settings.php
$GLOBALS[ 'blog_id' ] = $current_blog->blog_id;
$GLOBALS[ 'public' ] = $current_blog->public;
$GLOBALS[ 'current_site' ] = $current_site;
$GLOBALS[ 'current_blog' ] = $current_blog;
$GLOBALS[ 'path' ] = $current_blog->path;
$GLOBALS[ 'blogname' ] = $blogname;
}
/**
* get the blog details by domain and
* matching path segments
*
* @global $wpdb
* @param string $domain
* @param string $path
* @return \stdClass|FALSE
*/
function get_blog_details( $domain, $path ) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT
*
FROM
{$wpdb->blogs}
WHERE
domain = %s
AND
1 = INSTR( %s, path )
ORDER BY
CHAR_LENGTH( path ) DESC
LIMIT 1",
$domain,
$path
);
$current_blog = $wpdb->get_row( $query );
if ( empty( $current_blog ) )
return FALSE;
return $current_blog;
}