Discussion:
Multisite options
David Anderson
2014-01-20 10:02:01 UTC
Permalink
Hi,

I am working with code that wants to get the WordPress site's base table
prefix on a WPMU install. i.e. The original $table_prefix - not its
*current* value (which can be changed due to site switching).

This is available as $wpdb->base_prefix. However, this is only public by
default. The class itself does not explicitly mark it as public or
private. So, I reasoned that probable $wpdb->get_blog_prefix() is the
"proper" way to read it.

The code of that function is as follows:

function get_blog_prefix( $blog_id = null ) {

if ( is_multisite() ) {

if ( null === $blog_id )

$blog_id = $this->blogid;

$blog_id = (int) $blog_id;

if ( defined( 'MULTISITE' ) && ( 0 == $blog_id || 1 == $blog_id ) )

return $this->base_prefix;

else

return $this->base_prefix . $blog_id . '_';

} else {

return $this->base_prefix;

}

}

It thus looks like $wpdb->get_blog_prefix(0) is the way to get the value
I want. However, I am confused about the purpose of the block that
begins with if (defined( 'MULTISITE' )).

What is the specific purpose of the defined('MULTISITE') check? One
might guess that MULTISITE will always be defined if is_multisite()
passed. However, a look at is_multisite() shows that this is not
necessarily so:

function is_multisite() {

if ( defined( 'MULTISITE' ) )

return MULTISITE;

if ( defined( 'SUBDOMAIN_INSTALL' ) || defined( 'VHOST' ) || defined( 'SUNRISE' ) )

return true;

return false;

}

So, is_multisite() can return true even if MULTISITE is not true; there
are the additional situations where any of the 3 constants
SUBDOMAIN_INSTALL, VHOST or SUNRISE are defined (regardless of their value).

In that situation, $wpdb->get_blog_prefix() will actually return the
value of: $this->base_prefix . '0_'

That doesn't seem like it can be correct. On the other hand, the code
seems to be careful to bring about this result. It seems to be
deliberately engineered with the possibility that is_multisite() is true
but MULTISITE is not true.

Can someone give me a clue with what's going on here? I'm asking because
I've just come across a user for whom $wpdb->get_blog_prefix() does
actually return $this->base_prefix . '0_' - and he has no tables at all
with that prefix. It seems like the wrong result. I've not yet got the
information on the value of all those constants from him, but he says
it's a WPMU install. I'm looking into whether his install is set up
wrongly, but my question to wp-hackers is as to whether the
$wpdb->get_blog_prefix() is functioning as intended, or whether this is
a subtle bug, or whether I'm simply getting the base prefix in the wrong
way and should be using a different strategy.

Many thanks,
David
--
UpdraftPlus - best WordPress backups - http://updraftplus.com
WordShell - WordPress fast from the CLI - http://wordshell.net
Mike Burns
2014-01-20 16:07:06 UTC
Permalink
I don’t think there is a cleaner way to access that value then `$wpdb->base_prefix`. I wouldn’t use `$wpdb->get_blog_prefix()` — semantically that method is intended to retrieve the prefix for a specific blog, and passing 0 is a hack that won’t always work due to the issue you discovered.

The logic involving the `SUBDOMAIN_INSTALL` and `VHOST` constants is for backwards compatibility with pre-WP 3.0 installs.

Prior to WP 3.0 there was a separate code base dedicated to multisite — WPMU. There was no need for a `MULTISITE` constant as that was the only option you had. Instead, the `VHOST` constant was used to indicate whether or not it was a sub-domain or sub-directory install.

A fresh install of WPMU used `wp_1_` for the first blog prefix. When the single / MU codebases merged in WP 3.0, a fresh install used `wp_` for the first blog prefix. When converted to multisite (http://codex.wordpress.org/Create_A_Network), `wp_2_` was used for the next blog, so on and so on.

In order for WPMU-era installs to continue to work post-3.0, there are several places that use the definition of `MULTISITE` to decide which database prefix / uploads directory to use for the main site.

Best,
Mike
Paul Menard
2014-01-20 20:10:26 UTC
Permalink
Post by Mike Burns
A fresh install of WPMU used `wp_1_` for the first blog prefix. When the
single / MU codebases merged in WP 3.0, a fresh install used `wp_` for the
first blog prefix. When converted to multisite (
http://codex.wordpress.org/Create_A_Network), `wp_2_` was used for the
next blog, so on and so on.
Actually that is not accurate. The first install (http://somesite.com) has
prefix 'wp_'. This is the initial installed site which is also the database
used by the Network. When you enable Multisite you will see all the network
table have the same base prefix 'wp_', Then when you create a new blog (
http://site1.simesite.com) it will have the prefix wp_1_, then the next (
http://site2.somesite.com) will have prefix wp_2_

P-
Paul Menard
2014-01-20 20:13:09 UTC
Permalink
Mike

Actually you do know that 'MULTISITE' is define. When you enable Multisite
on a regular WordPress install it is one of the new settings you are
instructed to add to your wp-config.php.
http://codex.wordpress.org/Create_A_Network see step 4.

MULTISITE is a PHP define you can check using is_defined('MULTISITE').

Paul
Post by Paul Menard
Post by Mike Burns
A fresh install of WPMU used `wp_1_` for the first blog prefix. When the
single / MU codebases merged in WP 3.0, a fresh install used `wp_` for the
first blog prefix. When converted to multisite (
http://codex.wordpress.org/Create_A_Network), `wp_2_` was used for the
next blog, so on and so on.
Actually that is not accurate. The first install (http://somesite.com)
has prefix 'wp_'. This is the initial installed site which is also the
database used by the Network. When you enable Multisite you will see all
the network table have the same base prefix 'wp_', Then when you create a
new blog (http://site1.simesite.com) it will have the prefix wp_1_, then
the next (http://site2.somesite.com) will have prefix wp_2_
P-
Mika A Epstein
2014-01-20 20:27:05 UTC
Permalink
Paul, WPMU != WordPress Multisite :)

On old WPMU installs (i.e. pre 3.0) you did use the tables wp_1_ for site #1

On current multisite, Site #1 uses wp_ for it's prefix, and site #2 uses
wp_2_ and so on and so forth.

Mike is correct on that front as well as on the VHOST/MULTISITE define.
Older installs of WPMU that were upgraded to WordPress Multisite
generally don't have that MULTISITE define (which is good, because when
they do, WP bails when it can't find the wp_ tables it wants).

Old upgrades is uglies, man :/
20 January, 2014 at 12:13:09PM
Mike
Actually you do know that 'MULTISITE' is define. When you enable Multisite
on a regular WordPress install it is one of the new settings you are
instructed to add to your wp-config.php.
http://codex.wordpress.org/Create_A_Network see step 4.
MULTISITE is a PHP define you can check using is_defined('MULTISITE').
Paul
_______________________________________________
wp-hackers mailing list
http://lists.automattic.com/mailman/listinfo/wp-hackers
20 January, 2014 at 12:10:26PM
Actually that is not accurate. The first install (http://somesite.com) has
prefix 'wp_'. This is the initial installed site which is also the database
used by the Network. When you enable Multisite you will see all the network
table have the same base prefix 'wp_', Then when you create a new blog (
http://site1.simesite.com) it will have the prefix wp_1_, then the next (
http://site2.somesite.com) will have prefix wp_2_
P-
_______________________________________________
wp-hackers mailing list
http://lists.automattic.com/mailman/listinfo/wp-hackers
20 January, 2014 at 8:07:06AM
I don't think there is a cleaner way to access that value then
`$wpdb->base_prefix`. I wouldn't use `$wpdb->get_blog_prefix()` ---
semantically that method is intended to retrieve the prefix for a
specific blog, and passing 0 is a hack that won't always work due to
the issue you discovered.
The logic involving the `SUBDOMAIN_INSTALL` and `VHOST` constants is
for backwards compatibility with pre-WP 3.0 installs.
Prior to WP 3.0 there was a separate code base dedicated to multisite
--- WPMU. There was no need for a `MULTISITE` constant as that was the
only option you had. Instead, the `VHOST` constant was used to
indicate whether or not it was a sub-domain or sub-directory install.
A fresh install of WPMU used `wp_1_` for the first blog prefix. When
the single / MU codebases merged in WP 3.0, a fresh install used `wp_`
for the first blog prefix. When converted to multisite
(http://codex.wordpress.org/Create_A_Network), `wp_2_` was used for
the next blog, so on and so on.
In order for WPMU-era installs to continue to work post-3.0, there are
several places that use the definition of `MULTISITE` to decide which
database prefix / uploads directory to use for the main site.
Best,
Mike
_______________________________________________
wp-hackers mailing list
http://lists.automattic.com/mailman/listinfo/wp-hackers
20 January, 2014 at 2:02:01AM
Hi,
I am working with code that wants to get the WordPress site's base
table prefix on a WPMU install. i.e. The original $table_prefix - not
its *current* value (which can be changed due to site switching).
This is available as $wpdb->base_prefix. However, this is only public
by default. The class itself does not explicitly mark it as public or
private. So, I reasoned that probable $wpdb->get_blog_prefix() is the
"proper" way to read it.
function get_blog_prefix( $blog_id = null ) {
if ( is_multisite() ) {
if ( null === $blog_id )
$blog_id = $this->blogid;
$blog_id = (int) $blog_id;
if ( defined( 'MULTISITE' ) && ( 0 == $blog_id || 1 == $blog_id ) )
return $this->base_prefix;
else
return $this->base_prefix . $blog_id . '_';
} else {
return $this->base_prefix;
}
}
It thus looks like $wpdb->get_blog_prefix(0) is the way to get the
value I want. However, I am confused about the purpose of the block
that begins with if (defined( 'MULTISITE' )).
What is the specific purpose of the defined('MULTISITE') check? One
might guess that MULTISITE will always be defined if is_multisite()
passed. However, a look at is_multisite() shows that this is not
function is_multisite() {
if ( defined( 'MULTISITE' ) )
return MULTISITE;
if ( defined( 'SUBDOMAIN_INSTALL' ) || defined( 'VHOST' ) || defined( 'SUNRISE' ) )
return true;
return false;
}
So, is_multisite() can return true even if MULTISITE is not true;
there are the additional situations where any of the 3 constants
SUBDOMAIN_INSTALL, VHOST or SUNRISE are defined (regardless of their value).
In that situation, $wpdb->get_blog_prefix() will actually return the
value of: $this->base_prefix . '0_'
That doesn't seem like it can be correct. On the other hand, the code
seems to be careful to bring about this result. It seems to be
deliberately engineered with the possibility that is_multisite() is
true but MULTISITE is not true.
Can someone give me a clue with what's going on here? I'm asking
because I've just come across a user for whom $wpdb->get_blog_prefix()
does actually return $this->base_prefix . '0_' - and he has no tables
at all with that prefix. It seems like the wrong result. I've not yet
got the information on the value of all those constants from him, but
he says it's a WPMU install. I'm looking into whether his install is
set up wrongly, but my question to wp-hackers is as to whether the
$wpdb->get_blog_prefix() is functioning as intended, or whether this
is a subtle bug, or whether I'm simply getting the base prefix in the
wrong way and should be using a different strategy.
Many thanks,
David
--
Mika A Epstein (aka Ipstenu)
http://ipstenu.org | http://halfelf.org
David Anderson
2014-01-20 18:28:52 UTC
Permalink
Hi Mike,

Thanks very much for the response...
I don't think there is a cleaner way to access that value then `$wpdb->base_prefix`. I wouldn't use `$wpdb->get_blog_prefix()` --- semantically that method is intended to retrieve the prefix for a specific blog, and passing 0 is a hack that won't always work due to the issue you discovered.
I'm just a bit nervous about the fact that the value of $base_prefix
isn't marked as public. I've been bitten in the past by relying too much
on the current state of the code, running into later problems, and then
getting the feedback "that's not officially supported, you did it
wrong". The get_blog_prefix() function does seem to explicitly cater for
someone passing in the value "0", so that seems a little bit "official".
I suppose I could hedge my bets by testing the WordPress version (since
I know that it's effectively public up to WP 3.8)... and/or perhaps, by
testing for the inconsistency I've discovered in advance. Any further
thoughts? Anyone who works on WPDB in core have a comment on why
base_prefix isn't explicitly marked as public?

Based on your explanation below, if I've understood rightly, a WPMU
install from before 3.0 could legitimately lack the MULTISITE constant,
even after upgrading to a later version. Multisite status is detected
through other variables. However, I'm still unclear from your
explanation upon what exactly the part of the code inside
get_blog_prefix() that I highlighted is intended to be achieving. I'm
thinking it's buggy... let me explain...

if ( defined( 'MULTISITE' ) && ( 0 == $blog_id || 1 == $blog_id ) )
return $this->base_prefix;
else
return $this->base_prefix . $blog_id . '_';

In this part of the code, we know that is_multisite() is true. But, we
don't know that MULTISITE is defined. What is the purpose of the
MULTISITE check? Presumably, it is something to do with the legacy
situation, of a site that was upgraded from a pre-3.0 WPMU install. In
such cases, MULTISITE is not set, and the code enforces the outcome of
$this->base_prefix.$blog_id.'_' - even if $blog_id is 0 or 1. Perhaps
the missing piece of the jigsaw puzzle for me is that "in such installs,
$blog_id can never actually be 0 or 1" ???

Now, into the mix, I now have the information from my particular client.
In his situation, his constants (when read from a plugin page on the
network dashboard, on WordPress 3.8) are as follows (this is what he's
sent me):
MULTISITE: (undefined)
SUBDOMAIN_INSTALL: (undefined)
VHOST: no
SUNRISE: on

So, the values of VHOST and SUNRISE are causing is_multisite() to be
true. But MULTISITE is not set. His list of blogs includes one with ID 1
(that is, when he sent me his list of tables, he's got wp_1_*).

In this scenario, it appears to be impossible to get get_blog_prefix()
to return the base prefix, so my only option is to read
wpdb::base_prefix directly. But I wonder if I should also file a
bug/patch in trac so that the case of $blog_id == 0 always returns
base_prefix, regardless of MULTISITE - because get_blog_prefix() does
seem to want to be able to respond properly to the case $blog_id == 0,
and presently it's only doing so sometimes.

David
The logic involving the `SUBDOMAIN_INSTALL` and `VHOST` constants is for backwards compatibility with pre-WP 3.0 installs.
Prior to WP 3.0 there was a separate code base dedicated to multisite --- WPMU. There was no need for a `MULTISITE` constant as that was the only option you had. Instead, the `VHOST` constant was used to indicate whether or not it was a sub-domain or sub-directory install.
A fresh install of WPMU used `wp_1_` for the first blog prefix. When the single / MU codebases merged in WP 3.0, a fresh install used `wp_` for the first blog prefix. When converted to multisite (http://codex.wordpress.org/Create_A_Network), `wp_2_` was used for the next blog, so on and so on.
In order for WPMU-era installs to continue to work post-3.0, there are several places that use the definition of `MULTISITE` to decide which database prefix / uploads directory to use for the main site.
Best,
Mike
--
UpdraftPlus - best WordPress backups - http://updraftplus.com
WordShell - WordPress fast from the CLI - http://wordshell.net
Mike Burns
2014-01-20 20:32:07 UTC
Permalink
Post by David Anderson
Now, into the mix, I now have the information from my particular client.
In his situation, his constants (when read from a plugin page on the
network dashboard, on WordPress 3.8) are as follows (this is what he's
MULTISITE: (undefined)
SUBDOMAIN_INSTALL: (undefined)
VHOST: no
SUNRISE: on
Those look like typical settings from a WPMU-era wp-config.php. The logic you mention in `get_blog_prefix()` ensures that the main blog prefix is `wp_1_` for WPMU-era installs and `wp_` for WP 3.0+ installs. It does not, however, appropriately handle a `$blog_id` of 0 for this configuration as you have discovered.

You can file it as a bug, but I still maintain my original opinion that `get_blog_prefix()` isn’t the method you should be using. I defer to core devs for an “officially sanctioned” method for retrieving the base prefix — I don’t see one. Seems like a `get_table_prefix()` or `get_base_prefix()` method for the `wpdb` class would be more appropriate.
David Anderson
2014-01-20 21:13:36 UTC
Permalink
I don?t think there is a cleaner way to access that value then `$wpdb->base_prefix`. I wouldn?t use `$wpdb->get_blog_prefix()` ? semantically that method is intended to retrieve the prefix for a specific blog, and passing 0 is a hack that won?t always work due to the issue you discovered.
Having looked at and thought about this more, I think you're right.
Whatever the purpose of the 0 == $blog_id check in get_blog_prefix() was
when the coder added it, even if it was intended to be a way to get
$wpdb::base_prefix, this corner-case shows that it doesn't always work.

I've filed this ticket in trac, with a patch whose effect is to make
wpdb::base_prefix explicitly public, so that coders can know that
they're safe in relying on it: https://core.trac.wordpress.org/ticket/26887.

David
--
UpdraftPlus - best WordPress backups - http://updraftplus.com
WordShell - WordPress fast from the CLI - http://wordshell.net
Loading...