restore_current_blog() vs. multiple switch_to_blog() followed by removing $GLOBALS[‘_wp_switched_stack’]

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.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 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.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x