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 callspopulate_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 viarestore_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.