After every instance of switch_to_blog() you should call restore_current_blog() to restore the current (actually, previous) blog.

But if you're looping through two or more blogs and calling switch_to_blog() on each, is there any reason not to use an additional switch_to_blog() at the end of the loop to switch to the original blog rather than calling restore_current_blog() at each pass.

E.g.

Why not:

 $original_blog_id = get_current_blog_id();
 foreach( $blog_ids as $blog_id ){
    switch_to_blog( $blog_id );
    //Do stuff
 }
 switch_to_blog( $original_blog_id );

instead of:

 foreach( $blog_ids as $blog_id ){
    switch_to_blog( $blog_id );
    //Do stuff
    restore_current_blog_id();
 }
  • Now I understand this, thanks for correcting that answer of mine ;) Am revising everything. – brasofilo Mar 3 '13 at 10:55
up vote 18 down vote accepted

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.

If you view the source code for both functions you will see those functions push/pop data into a global called $GLOBALS['_wp_switched_stack']. If you do not call restore_current_blog() after every switch_to_blog(), $GLOBALS['_wp_switched_stack'] will be non-empty. If $GLOBALS['_wp_switched_stack'] is non-empty WP thinks it is in a switched mode, even if you switched back to the original blog using switch_to_blog(). The switched mode function is ms_is_switched() and it affects wp_upload_dir(). If wp_upload_dir() thinks it is in a switched mode, it can return data that is incorrect. wp_upload_dir() builds URLs for the site, so it is a very critical function.

FYI, I ran into this problem using a plugin that used switch_to_blog(). The plugin did not use restore_current_blog() to restore, instead it used switch_to_blog() to switch back to the original site. With the plugin active, all my site urls that were generated were incorrect.

This is the correct use:

 foreach( $blog_ids as $blog_id ){
    switch_to_blog( $blog_id );
    //Do stuff
    restore_current_blog();
 }
  • Thanks, I've not had chance to work through the soup of constants & logic that wp_upload_dir() employs to generate urls, but I shall take your word that this does indeed result in buggy behaviour. In any case, the existence of ms_is_switched() means my alternative approach results in the function not behaving as expected and could break plug-ins as well as core. Thanks – Stephen Harris Nov 17 '13 at 22:59
  • 1
    If this is true, then the Codex page for restore_current_blog() needs an update, since it says that for multiple switches, one need only save the current $blog_id and then use multiple switch_to_blog() calls. – Pat J Nov 21 '13 at 16:54

If you want to run over multiple blogs there is no need to restore the previous blog each time. The only thing that grows is $GLOBALS['_wp_switched_stack'] – an array with blog IDs, nothing to worry about.

But keep in mind, restore_current_blog() will not work anymore after the second switch, because it uses the previous blog – which is not the first blog then. So store the first blog ID, and call …

switch_to_blog( $first_blog_id ); 
unset ( $GLOBALS['_wp_switched_stack'] );
$GLOBALS['switched'] = false; 

… instead of restore_current_blog() when you are done. The global variables must be reset, or you will run into the issues mentioned by @user42826.

The performance impact is huge. I’ve run some tests on a local installation with 12 sites:

$sites = wp_get_sites();

print '<pre>' . count( $sites ) . " sites\n";

timer_start();

print 'With restore_current_blog():    ';

foreach ( $sites as $site ) {
    switch_to_blog( $site[ 'blog_id' ] );
    restore_current_blog();
}

timer_stop( 1, 9 );

print "\nWithout restore_current_blog(): ";

timer_start();

$current_site = get_current_blog_id();

foreach ( $sites as $site ) {
    switch_to_blog( $site[ 'blog_id' ] );
}

switch_to_blog( $current_site );
$GLOBALS['_wp_switched_stack'] = array();
$GLOBALS['switched']           = FALSE;

timer_stop( 1, 9 );

print '</pre>';

Result:

12 sites
With restore_current_blog():    0.010648012
Without restore_current_blog(): 0.005203962

Using restore_current_blog() after each switch doubles the time that is needed just for switching.

  • Thought there wasn't any reason not to. Was confused why restore_current_blog() didn't just retrive the previous blog ID and call switch_to_blog() - a brief look at the code source and it seems there's a bit of code duplication... – Stephen Harris Mar 2 '13 at 20:52
  • 3
    I don't think modifying the globals directly is a good idea, because you're coupling your code to Core's internals, which isn't future-proof. It's better to use the API properly. – Ian Dunn Sep 29 '14 at 17:17
  • 2
    @IanDunn Just for the record: switch_to_blog() is a very limited (broken) API anyway. If WordPress ever fixes that, we have to refactor our code anyway. And WordPress will never give up its beloved globals. – fuxia Feb 2 '15 at 0:54
  • 2
    @IanDunn I don't think modifying the globals directly is a good idea, don't tell that to wp core developers ;) – Ejaz Feb 2 '16 at 19:14
  • 1
    @J.D. Of course, you need to be context aware. In the case of an already switched state you might have even to maintain the correct index of the stack. I would probably search for a way to avoid that. On the other hand, this is WordPress, so there might be no other way … – fuxia Jul 20 '16 at 19:28

Thanks to @toscho answer. This request in queue of WP - see updates here. Till that is fixed in WP, if anyone desperately wants to use standard restore_current_blog(), then here is another method (please correct if I am wrong):

make your function, i.e.

function restore_original_blog_X(){

    if(!empty(($GLOBALS['_wp_switched_stack'][0])){
        $GLOBALS['blog_id']= $GLOBALS['_wp_switched_stack'][0];
        $GLOBALS['_wp_switched_stack'] = array($GLOBALS['_wp_switched_stack'][0]);
        restore_current_blog();
    }

}

and execute only once when you finish your multiple switches. (more: wp-includes/ms-blogs.php )

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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