Bounty Ended with 250 reputation awarded by Rarst
2 added 99 characters in body
source | link

Importantly to note is that wp_install_defaults()wp_install_defaults() flushes rules exactly as I've suggested above above:

Importantly to note is that wp_install_defaults() flushes rules exactly as I've suggested above above:

Importantly to note is that wp_install_defaults() flushes rules exactly as I've suggested above above:

1
source | link

Note: this is an incomplete answer which will be expanded upon incrementally


The only reliable way to flush rewrite rules in multisite, without potentially destroying the permalink structure of the primary and or any other blog context (depending upon how and what you a switching to and from) is to flush rewrite rules in a given context like so:

global $wp_rewrite;
$wp_rewrite->init(); //important...
$wp_rewrite->flush_rules();

The above ensures that correct permalink structure for the given context is retrieved and set prior to constructing the rewrite rules and commiting the changes to the database.

This does not apply to single site where context does not matter, because there is only one context.

flush_rewrite_rules() in my opinion is flawed in the premise that it assumes the correct context, but does not take into account our use of switch_to_blog for which completely changes the context and leaves us in dangerous territory if we try to flush rules, potentially.

This is what the internals of flush_rewrite_rules() looks like:

function flush_rewrite_rules( $hard = true ) {
    global $wp_rewrite;
    $wp_rewrite->flush_rules( $hard );
}

I cannot think of a reason why it shouldn't look like this:

function flush_rewrite_rules( $hard = true ) {
    global $wp_rewrite;
    $wp_rewrite->init(); //hello....
    $wp_rewrite->flush_rules( $hard );
}

...especially when you consider that the constructor of WP_Rewrite does what? It does this...

public function __construct() {
    $this->init();
}

Touching on your first point of concern to further this line of though,

So how would plugin go about reliably flushing rewrite rules in multisite:

  • When new site is created, for the site?

Let's look at what WordPress core will notably call during this process:

  • first wpmu_create_blog()
  • which then calls install_blog() which in turn calls populate_options()
  • then populate_options() sets default permalink structure in options table
  • after install_blog() has ran, wp_install_defaults() then gets called
  • then wp_install_defaults() flushes the rewrite rules for the newly created site before finally switching back to the current blog via restore_current_blog().

Importantly to note is that wp_install_defaults() flushes rules exactly as I've suggested above above:

$wp_rewrite->init();
$wp_rewrite->flush_rules();

...because that's the only way to be sure the correct permalink_structure and rules are built for the current context.

Also in the problem evidenced within the Github issue, the reason why the user experienced the following behavior:

When a new site is created it breaks the post level permalinks only on the top level site - in most permalink configurations but not all:

These 2 formats work correctly.

Default - Works as expected

Day & Name - Works as expected

...is because if the primary blog has a Day & Name permalink structure /%year%/%monthnum%/%day%/%postname%/, when a new site is created, it too has a Day & Name permalink structure /%year%/%monthnum%/%day%/%postname%/ by default which is why no notable problem presents itself when the Yoast SEO plugin flushes rewrite rules on the shutdown hook.