0

Related to this answer, which states

After every instance of switch_to_blog() you need to call restore_current_blog() otherwise WP will think it is in a "switched" mode and can potentially return incorrect data.

I've done some testing, and can confirm that this is an issue. (It also answers a question I've had at work regarding file upload URLs, so two big thumbs up for that.)

In my testing (detailed below) I found that there were two ways to proceed:

  1. Always pair switch_to_blog() with restore_current_blog()
  2. Use a chain of switch_to_blog(), with the last one switching back to the blog where you started from, and then unset( $GLOBALS['_wp_switched_stack'] ); at the end.

My question: Is method #2 in keeping with WordPress development best practices, or is it an ugly hack?

My Testing

I added the following code to functions.php on one of my WordPress Multisite installations:

add_action( 'shutdown', 'pj_stb_test' );
function pj_stb_test() {
    if( ! current_user_can( 'update_core' ) )
        return;

    $home = get_current_blog_id();
    $checklist = array(
        'constants' => array( 'UPLOADS', 'MULTISITE', 'BLOGUPLOADDIR' ),
        'functions' => array( 'ms_is_switched', 'wp_upload_dir' ),
    );
    $site_ids = array( 1, 2, 3 );

    echo( "switch_to_blog() chain:<br />" );
    foreach( $site_ids as $id ) {
        switch_to_blog( $id );
    }
    switch_to_blog( $home );
    _pj_dump( $checklist );

    echo( 'switch_to_blog() chain followed by 
           unset( $GLOBALS[\'_wp_switched_stack\'] )<br />' );
    foreach( $site_ids as $id ) {
        switch_to_blog( $id );
    }
    switch_to_blog( $home );
    unset( $GLOBALS['_wp_switched_stack'] );
    _pj_dump( $checklist );

    echo( 'switch_to_blog() / restore_current_blog() pairings<br />' );
    foreach( $site_ids as $id ) {
        switch_to_blog( $id );
        restore_current_blog();
    }
    _pj_dump( $checklist );

function _pj_dump( $checklist ) {
    $constants = $checklist['constants'];
    $functions = $checklist['functions'];
    echo( "<p>Constants:</p>" );
    echo( "<pre>\n" );
    foreach( $constants as $c ) {
        echo( $c . ': ' );
        var_dump( constant( $c ) );
        echo( "\n" );
    }
    foreach( $functions as $f ) {
        echo( $f . ': ' );
        var_dump( call_user_func( $f ) );
        echo( "\n" );
    }
}

The output that was returned on an arbitrary site in my network:

switch_to_blog() chain:
UPLOADS: string(30) "wp-content/blogs.dir/94/files/"
MULTISITE: bool(true)
BLOGUPLOADDIR: string(50) "/path/to/wp/wp-content/blogs.dir/94/files/"
ms_is_switched: bool(true)
wp_upload_dir: array(6) {
  ["path"]=>
  string(57) "/path/to/wp/wp-content/blogs.dir/94/files/2013/11"
  ["url"]=>
  string(74) "http://example.com/my-site/wp-content/blogs.dir/94/files/2013/11"
  ["subdir"]=>
  string(8) "/2013/11"
  ["basedir"]=>
  string(49) "/path/to/wp/wp-content/blogs.dir/94/files"
  ["baseurl"]=>
  string(66) "http://example.com/my-site/wp-content/blogs.dir/94/files"
  ["error"]=>
  bool(false)
} 

switch_to_blog() chain followed by unset( $GLOBALS['_wp_switched_stack'] )
UPLOADS: string(30) "wp-content/blogs.dir/94/files/"
MULTISITE: bool(true)
BLOGUPLOADDIR: string(50) "/path/to/wp/wp-content/blogs.dir/94/files/"
ms_is_switched: bool(false)
wp_upload_dir: array(6) {
  ["path"]=>
  string(57) "/path/to/wp/wp-content/blogs.dir/94/files/2013/11"
  ["url"]=>
  string(50) "http://example.com/my-site/files/2013/11"
  ["subdir"]=>
  string(8) "/2013/11"
  ["basedir"]=>
  string(49) "/path/to/wp/wp-content/blogs.dir/94/files"
  ["baseurl"]=>
  string(42) "http://example.com/my-site/files"
  ["error"]=>
  bool(false)
} 

switch_to_blog() / restore_current_blog() pairings
UPLOADS: string(30) "wp-content/blogs.dir/94/files/"
MULTISITE: bool(true)
BLOGUPLOADDIR: string(50) "/path/to/wp/wp-content/blogs.dir/94/files/"
ms_is_switched: bool(false)
wp_upload_dir: array(6) {
  ["path"]=>
  string(57) "/path/to/wp/wp-content/blogs.dir/94/files/2013/11"
  ["url"]=>
  string(50) "http://example.com/my-site/files/2013/11"
  ["subdir"]=>
  string(8) "/2013/11"
  ["basedir"]=>
  string(49) "/path/to/wp/wp-content/blogs.dir/94/files"
  ["baseurl"]=>
  string(42) "http://example.com/my-site/files"
  ["error"]=>
  bool(false)
}

Sorry for the verbosity, but I wanted to make sure I had everything in here.

1

Short answer is NO. That is an ugly hack =) More global variables are affect than the ones that you are var_dump'ing. Here are the globals that I found affected: $wpdb, $wp_roles, $wp_object_cache, $global_groups, $GLOBALS['_wp_switched_stack'], $GLOBALS['blog_id'], and $GLOBALS['table_prefix']. There could be more. I found that $wpdb was another major variable that was altered by switch_to_blog(). Try var_dump'ing $wpdb in your test and you will see the effect. Any APIs relying on those globals could be affected if you do not "Always pair switch_to_blog() with restore_current_blog()"

Long answer is "depends on the situation." I would say the statement "After every instance of switch_to_blog() you need to call restore_current_blog()" is the best practice and generally true. But there are situations where you do not need to return to the originial blog or do restore_current_blog(). For example, I created an admin plugin which altered a users role across all blogs. It iterated through all the blogs in the network using switch_to_blog, called other WP APIs (that did not rely on those globals), and ended immediately. Hence no need to restore_current_blog(). YMMV depending on where and how switch_to_blog() is used.

1
  • As far as your second ¶ goes -- I have a similar situation where I use switch_to_blog() repeatedly to generate a network-wide breadcrumb trail, and I think one of the consequences is that wp_upload_dir() is returning the full /wp-content/blogs.dir/[xxx]/files system path, rather than the /files path I expect. So I guess I'll need to add some restore_current_blog()s to that code.
    – Pat J
    Nov 22 '13 at 14:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.