[wp-trac] [WordPress Trac] #37958: Improve looping through sites and restoring

WordPress Trac noreply at wordpress.org
Tue Sep 6 17:58:27 UTC 2016


#37958: Improve looping through sites and restoring
------------------------------------+-----------------------------
 Reporter:  tfrommen                |      Owner:
     Type:  enhancement             |     Status:  new
 Priority:  normal                  |  Milestone:  Awaiting Review
Component:  Networks and Sites      |    Version:
 Severity:  normal                  |   Keywords:
  Focuses:  multisite, performance  |
------------------------------------+-----------------------------
 [https://wordpress.slack.com/archives/core-multisite/p1473179575000111 As
 discussed] in the recent multisite ~~hours~~ minutes, I would like to
 propose (and discuss) a better means to looping through a number of (or
 even all) sites and finally restoring to the state before the loop.

 The naive approach looks like the following:

 {{{#!php
 <?php
 foreach ( $some_or_all_site_ids as $site_id ) {
     switch_to_blog( $site_id );

     // Do some (maybe expensive) things on the site.

     restore_current_blog();
 }
 }}}

 This is not ideal as in all but the last case we don't really want to
 restore, but just switch to the next site.

 An ''improved'' version then would look like this:


 {{{#!php
 <?php
 $__site_id  = get_current_blog_id();
 $__stack    = $GLOBALS['_wp_switched_stack'];
 $__switched = $GLOBALS['switched'];

 foreach ( $some_or_all_site_ids as $site_id ) {
     switch_to_blog( $site_id );

     // Do some (maybe expensive) things on the site.
 }

 switch_to_blog( $__site_id );
 $GLOBALS['_wp_switched_stack'] = $__stack;
 $GLOBALS['switched']           = $__switched;
 }}}

 This, however, doesn't look nice. Moreover, you would have to do this for
 every loop again and again.

 So, why not abstract out the logic into a statically creatable
 ''snapshot'' of the current network state?

 [https://gist.github.com/tfrommen/51d80f6d327d4d254ad9ace09b8d3fdc My
 suggestion] for such a class looks like the following (which is already in
 use in the wild):

 {{{#!php
 <?php
 /**
  * Save and restore the current network state.
  *
  * By using this class, you can avoid unnecessary
  * switch_to_blog()-restore_current_blog()-switch_to_blog()-... excesses.
  */
 class NetworkState {

         /**
          * @var int
          */
         private $site_id;

         /**
          * @var int[]
          */
         private $stack;

         /**
          * @var bool
          */
         private $switched;

         /**
          * Constructor. Sets up the properties.
          */
         public function __construct() {

                 global $_wp_switched_stack, $switched;

                 $this->site_id = get_current_blog_id();

                 $this->stack = $_wp_switched_stack;

                 $this->switched = $switched;
         }

         /**
          * Returns a new instance representing the current network state.
          *
          * @return static Network state object.
          */
         public static function create() {

                 return new static();
         }

         /**
          * Restores the saved network state.
          *
          * @return void
          */
         public function restore() {

                 switch_to_blog( $this->site_id );

                 $GLOBALS['_wp_switched_stack'] = $this->stack;

                 $GLOBALS['switched'] = $this->switched;
         }
 }
 }}}

 With this class, the `foreach`-loop code from before can become this:

 {{{#!php
 <?php
 $network_state = NetworkState:create();

 foreach ( $some_or_all_site_ids as $site_id ) {
     switch_to_blog( $site_id );

     // Do some (maybe expensive) things on the site.
 }

 $network_state->restore();
 }}}

 No matter what happens in the loop (and thus in any called function or
 method), we restore to the exact same state the network was in before the
 loop.

 The above implementation works with the (currently available and used)
 globals. As soon as there is some other way (see #37699), this can easily
 be adapted as it's internals only.

 The naming is just a suggestion, and we can, of course, baptize all the
 things differently.

 Core could make use of it, too. For example in the
 [https://core.trac.wordpress.org/browser/trunk/src/wp-
 admin/includes/ms.php?rev=38334#L221 `wpmu_delete_user()] function, during
 [https://core.trac.wordpress.org/browser/trunk/src/wp-
 admin/network/upgrade.php?rev=38229#L64 Network upgrade], or in the
 [https://core.trac.wordpress.org/browser/trunk/src/wp-includes/admin-
 bar.php?rev=38470#L461 wp_admin_bar_my_sites_menu()] function (!), which
 means: On. Every. Single. Page. Load.

 I'd be happy to provide a full patch against trunk. I just wanted to
 propose this first and give opportunity to discuss this.

--
Ticket URL: <https://core.trac.wordpress.org/ticket/37958>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list