Term splitting routine should be run in a separate process, triggered via wp-cron.
[32814] introduced a routine to split shared terms, which was run during the regular WP database upgrade. This turned out to be problematic because plugins are not loaded during the db upgrade (due to `WP_INSTALLING`), with the result that plugins were not able to hook into the 'split_shared_term' action during the bulk split. We work around this limitation by moving the term splitting routine to a separate process, triggered by a wp-cron hook. Props boonebgorges, Chouby, peterwilsoncc, pento, dd32. Fixes #30261. git-svn-id: https://develop.svn.wordpress.org/trunk@33615 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
23eaa36145
commit
f18355e66e
@ -1505,8 +1505,9 @@ function upgrade_430() {
|
||||
upgrade_430_fix_comments();
|
||||
}
|
||||
|
||||
// Shared terms are split in a separate process.
|
||||
if ( $wp_current_db_version < 32814 ) {
|
||||
split_all_shared_terms();
|
||||
wp_schedule_single_event( time() + ( 1 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' );
|
||||
}
|
||||
|
||||
if ( $wp_current_db_version < 33055 && 'utf8mb4' === $wpdb->charset ) {
|
||||
@ -1880,76 +1881,6 @@ function maybe_convert_table_to_utf8mb4( $table ) {
|
||||
return $wpdb->query( "ALTER TABLE $table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits all shared taxonomy terms.
|
||||
*
|
||||
* @since 4.3.0
|
||||
*
|
||||
* @global wpdb $wpdb WordPress database abstraction object.
|
||||
*/
|
||||
function split_all_shared_terms() {
|
||||
global $wpdb;
|
||||
|
||||
// Get a list of shared terms (those with more than one associated row in term_taxonomy).
|
||||
$shared_terms = $wpdb->get_results(
|
||||
"SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt
|
||||
LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
|
||||
GROUP BY t.term_id
|
||||
HAVING term_tt_count > 1"
|
||||
);
|
||||
|
||||
if ( empty( $shared_terms ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rekey shared term array for faster lookups.
|
||||
$_shared_terms = array();
|
||||
foreach ( $shared_terms as $shared_term ) {
|
||||
$term_id = intval( $shared_term->term_id );
|
||||
$_shared_terms[ $term_id ] = $shared_term;
|
||||
}
|
||||
$shared_terms = $_shared_terms;
|
||||
|
||||
// Get term taxonomy data for all shared terms.
|
||||
$shared_term_ids = implode( ',', array_keys( $shared_terms ) );
|
||||
$shared_tts = $wpdb->get_results( "SELECT * FROM {$wpdb->term_taxonomy} WHERE `term_id` IN ({$shared_term_ids})" );
|
||||
|
||||
// Split term data recording is slow, so we do it just once, outside the loop.
|
||||
$suspend = wp_suspend_cache_invalidation( true );
|
||||
$split_term_data = get_option( '_split_terms', array() );
|
||||
$skipped_first_term = $taxonomies = array();
|
||||
foreach ( $shared_tts as $shared_tt ) {
|
||||
$term_id = intval( $shared_tt->term_id );
|
||||
|
||||
// Don't split the first tt belonging to a given term_id.
|
||||
if ( ! isset( $skipped_first_term[ $term_id ] ) ) {
|
||||
$skipped_first_term[ $term_id ] = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! isset( $split_term_data[ $term_id ] ) ) {
|
||||
$split_term_data[ $term_id ] = array();
|
||||
}
|
||||
|
||||
// Keep track of taxonomies whose hierarchies need flushing.
|
||||
if ( ! isset( $taxonomies[ $shared_tt->taxonomy ] ) ) {
|
||||
$taxonomies[ $shared_tt->taxonomy ] = 1;
|
||||
}
|
||||
|
||||
// Split the term.
|
||||
$split_term_data[ $term_id ][ $shared_tt->taxonomy ] = _split_shared_term( $shared_terms[ $term_id ], $shared_tt, false );
|
||||
}
|
||||
|
||||
// Rebuild the cached hierarchy for each affected taxonomy.
|
||||
foreach ( array_keys( $taxonomies ) as $tax ) {
|
||||
delete_option( "{$tax}_children" );
|
||||
_get_term_hierarchy( $tax );
|
||||
}
|
||||
|
||||
wp_suspend_cache_invalidation( $suspend );
|
||||
update_option( '_split_terms', $split_term_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all options as it was for 1.2.
|
||||
*
|
||||
|
@ -328,9 +328,11 @@ add_filter( 'determine_current_user', 'wp_validate_auth_cookie' );
|
||||
add_filter( 'determine_current_user', 'wp_validate_logged_in_cookie', 20 );
|
||||
|
||||
// Split term updates.
|
||||
add_action( 'admin_init', '_wp_check_for_scheduled_split_terms' );
|
||||
add_action( 'split_shared_term', '_wp_check_split_default_terms', 10, 4 );
|
||||
add_action( 'split_shared_term', '_wp_check_split_terms_in_menus', 10, 4 );
|
||||
add_action( 'split_shared_term', '_wp_check_split_nav_menu_terms', 10, 4 );
|
||||
add_action( 'wp_split_shared_term_batch', '_wp_batch_split_terms' );
|
||||
|
||||
/**
|
||||
* Filters formerly mixed into wp-includes
|
||||
|
@ -4249,12 +4249,6 @@ function _split_shared_term( $term_id, $term_taxonomy_id, $record = true ) {
|
||||
$term_taxonomy_id = intval( $term_taxonomy->term_taxonomy_id );
|
||||
}
|
||||
|
||||
// Don't try to split terms if database schema does not support shared slugs.
|
||||
$current_db_version = get_option( 'db_version' );
|
||||
if ( $current_db_version < 30133 ) {
|
||||
return $term_id;
|
||||
}
|
||||
|
||||
// If there are no shared term_taxonomy rows, there's nothing to do here.
|
||||
$shared_tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy tt WHERE tt.term_id = %d AND tt.term_taxonomy_id != %d", $term_id, $term_taxonomy_id ) );
|
||||
|
||||
@ -4262,6 +4256,15 @@ function _split_shared_term( $term_id, $term_taxonomy_id, $record = true ) {
|
||||
return $term_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that the term_taxonomy_id passed to the function is actually associated with the term_id.
|
||||
* If there's a mismatch, it may mean that the term is already split. Return the actual term_id from the db.
|
||||
*/
|
||||
$check_term_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) );
|
||||
if ( $check_term_id != $term_id ) {
|
||||
return $check_term_id;
|
||||
}
|
||||
|
||||
// Pull up data about the currently shared slug, which we'll use to populate the new one.
|
||||
if ( empty( $shared_term ) ) {
|
||||
$shared_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.* FROM $wpdb->terms t WHERE t.term_id = %d", $term_id ) );
|
||||
@ -4338,6 +4341,116 @@ function _split_shared_term( $term_id, $term_taxonomy_id, $record = true ) {
|
||||
return $new_term_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a batch of shared taxonomy terms.
|
||||
*
|
||||
* @since 4.3.0
|
||||
*
|
||||
* @global wpdb $wpdb WordPress database abstraction object.
|
||||
*/
|
||||
function _wp_batch_split_terms() {
|
||||
global $wpdb;
|
||||
|
||||
$lock_name = 'term_split.lock';
|
||||
|
||||
// Try to lock.
|
||||
$lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_name, time() ) );
|
||||
|
||||
if ( ! $lock_result ) {
|
||||
$lock_result = get_option( $lock_name );
|
||||
|
||||
// Bail if we were unable to create a lock, or if the existing lock is still valid.
|
||||
if ( ! $lock_result || ( $lock_result > ( time() - HOUR_IN_SECONDS ) ) ) {
|
||||
wp_schedule_single_event( time() + ( 5 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the lock, as by this point we've definitely got a lock, just need to fire the actions.
|
||||
update_option( $lock_name, time() );
|
||||
|
||||
// Get a list of shared terms (those with more than one associated row in term_taxonomy).
|
||||
$shared_terms = $wpdb->get_results(
|
||||
"SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt
|
||||
LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
|
||||
GROUP BY t.term_id
|
||||
HAVING term_tt_count > 1
|
||||
LIMIT 20"
|
||||
);
|
||||
|
||||
// No more terms, we're done here.
|
||||
if ( ! $shared_terms ) {
|
||||
update_option( 'finished_splitting_shared_terms', true );
|
||||
delete_option( $lock_name );
|
||||
return;
|
||||
}
|
||||
|
||||
// Shared terms found? We'll need to run this script again.
|
||||
wp_schedule_single_event( time() + ( 2 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' );
|
||||
|
||||
// Rekey shared term array for faster lookups.
|
||||
$_shared_terms = array();
|
||||
foreach ( $shared_terms as $shared_term ) {
|
||||
$term_id = intval( $shared_term->term_id );
|
||||
$_shared_terms[ $term_id ] = $shared_term;
|
||||
}
|
||||
$shared_terms = $_shared_terms;
|
||||
|
||||
// Get term taxonomy data for all shared terms.
|
||||
$shared_term_ids = implode( ',', array_keys( $shared_terms ) );
|
||||
$shared_tts = $wpdb->get_results( "SELECT * FROM {$wpdb->term_taxonomy} WHERE `term_id` IN ({$shared_term_ids})" );
|
||||
|
||||
// Split term data recording is slow, so we do it just once, outside the loop.
|
||||
$suspend = wp_suspend_cache_invalidation( true );
|
||||
$split_term_data = get_option( '_split_terms', array() );
|
||||
$skipped_first_term = $taxonomies = array();
|
||||
foreach ( $shared_tts as $shared_tt ) {
|
||||
$term_id = intval( $shared_tt->term_id );
|
||||
|
||||
// Don't split the first tt belonging to a given term_id.
|
||||
if ( ! isset( $skipped_first_term[ $term_id ] ) ) {
|
||||
$skipped_first_term[ $term_id ] = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! isset( $split_term_data[ $term_id ] ) ) {
|
||||
$split_term_data[ $term_id ] = array();
|
||||
}
|
||||
|
||||
// Keep track of taxonomies whose hierarchies need flushing.
|
||||
if ( ! isset( $taxonomies[ $shared_tt->taxonomy ] ) ) {
|
||||
$taxonomies[ $shared_tt->taxonomy ] = 1;
|
||||
}
|
||||
|
||||
// Split the term.
|
||||
$split_term_data[ $term_id ][ $shared_tt->taxonomy ] = _split_shared_term( $shared_terms[ $term_id ], $shared_tt, false );
|
||||
}
|
||||
|
||||
// Rebuild the cached hierarchy for each affected taxonomy.
|
||||
foreach ( array_keys( $taxonomies ) as $tax ) {
|
||||
delete_option( "{$tax}_children" );
|
||||
_get_term_hierarchy( $tax );
|
||||
}
|
||||
|
||||
wp_suspend_cache_invalidation( $suspend );
|
||||
update_option( '_split_terms', $split_term_data );
|
||||
|
||||
delete_option( $lock_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* In order to avoid the wp_batch_split_terms() job being accidentally removed,
|
||||
* check that it's still scheduled while we haven't finished splitting terms.
|
||||
*
|
||||
* @ignore
|
||||
* @since 4.3.0
|
||||
*/
|
||||
function _wp_check_for_scheduled_split_terms() {
|
||||
if ( ! get_option( 'finished_splitting_shared_terms' ) && ! wp_next_scheduled( 'wp_batch_split_terms' ) ) {
|
||||
wp_schedule_single_event( 'wp_batch_split_terms', time() + MINUTE_IN_SECONDS );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check default categories when a term gets split to see if any of them need to be updated.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user