register_activation_hook( ESLAM_PLUGIN_FILE, function() { /* * Activation must stay lightweight. Existing sites can contain very large * analytics/meta tables, so every existing-row migration and every production * schema repair is queued for bounded background work. */ eslam_register_track_visit_endpoint(); eslam_get_daily_maintenance_cron_key(); /* * Create only genuinely missing tables during activation. The ensure() * functions now refuse to run existing-table schema/data repairs from an * activation request, so this keeps fresh installs functional without * reintroducing the production-table risk. */ if ( function_exists( 'eslam_ensure_author_country_stats_table' ) ) { eslam_ensure_author_country_stats_table(); } if ( function_exists( 'eslam_ensure_geo_cache_table' ) ) { eslam_ensure_geo_cache_table(); } if ( function_exists( 'eslam_ensure_author_unique_visits_table' ) ) { eslam_ensure_author_unique_visits_table(); } if ( function_exists( 'eslam_ensure_notifications_table' ) ) { eslam_ensure_notifications_table(); } if ( function_exists( 'eslam_achievements_ensure_tables' ) ) { eslam_achievements_ensure_tables(); } if ( function_exists( 'eslam_ensure_security_logs_table' ) ) { eslam_ensure_security_logs_table(); } if ( function_exists( 'eslam_schedule_background_setup' ) ) { eslam_schedule_background_setup( 20 ); } if ( function_exists( 'eslam_database_integrity_schedule' ) ) { eslam_database_integrity_schedule(); } if ( function_exists( 'eslam_achievements_schedule_cron' ) ) { eslam_achievements_schedule_cron(); } if ( function_exists( 'eslam_schedule_legacy_prefix_migration' ) ) { eslam_schedule_legacy_prefix_migration( 30 ); } if ( function_exists( 'eslam_schedule_legacy_notifications_migration' ) ) { eslam_schedule_legacy_notifications_migration( 45 ); } if ( function_exists( 'eslam_schedule_geo_data_migration' ) ) { eslam_schedule_geo_data_migration( 60 ); } if ( function_exists( 'eslam_schedule_country_data_migration' ) ) { eslam_schedule_country_data_migration( 75 ); } if ( function_exists( 'eslam_schedule_unique_visits_data_migration' ) ) { eslam_schedule_unique_visits_data_migration( 90 ); } eslam_schedule_daily_tracking_cleanup(); eslam_schedule_daily_geo_cache_cleanup(); flush_rewrite_rules(); } ); register_deactivation_hook( ESLAM_PLUGIN_FILE, function() { if ( function_exists( 'eslam_database_integrity_unschedule' ) ) { eslam_database_integrity_unschedule(); } eslam_unschedule_daily_tracking_cleanup(); eslam_unschedule_daily_geo_cache_cleanup(); $hooks = array( ESLAM_NOTIFICATIONS_MIGRATION_HOOK, ESLAM_DB_PREFIX_MIGRATION_HOOK, ESLAM_GEO_DATA_MIGRATION_HOOK, ESLAM_COUNTRY_DATA_MIGRATION_HOOK, ESLAM_UNIQUE_VISITS_DATA_MIGRATION_HOOK, ESLAM_BACKGROUND_SETUP_HOOK, ESLAM_DB_INTEGRITY_REPAIR_HOOK, ESLAM_ACHIEVEMENTS_BACKFILL_HOOK, ); foreach ( $hooks as $hook ) { $timestamp = wp_next_scheduled( $hook ); while ( $timestamp ) { wp_unschedule_event( $timestamp, $hook ); $timestamp = wp_next_scheduled( $hook ); } } delete_option( ESLAM_BACKGROUND_SETUP_STATE_OPTION ); delete_option( 'eslam_notifications_migration_state' ); if ( function_exists( 'eslam_achievements_unschedule_cron' ) ) { eslam_achievements_unschedule_cron(); } flush_rewrite_rules(); } ); add_action( 'init', function() { /* * Never perform an existing-table schema upgrade from the ordinary init hook. * If a schema version is stale, queue the background setup worker instead. */ if ( get_option( ESLAM_AUTHOR_COUNTRY_TABLE_OPTION, '' ) !== ESLAM_AUTHOR_COUNTRY_TABLE_VERSION || get_option( ESLAM_GEO_CACHE_TABLE_OPTION, '' ) !== ESLAM_GEO_CACHE_TABLE_VERSION || get_option( ESLAM_UNIQUE_VISITS_TABLE_OPTION, '' ) !== ESLAM_UNIQUE_VISITS_TABLE_VERSION || get_option( ESLAM_NOTIFICATIONS_TABLE_OPTION, '' ) !== ESLAM_NOTIFICATIONS_TABLE_VERSION ) { if ( function_exists( 'eslam_schedule_background_setup' ) ) { eslam_schedule_background_setup( 30 ); } } eslam_get_daily_maintenance_cron_key(); eslam_schedule_daily_tracking_cleanup(); eslam_schedule_daily_geo_cache_cleanup(); if ( function_exists( 'eslam_schedule_legacy_prefix_migration' ) ) { eslam_schedule_legacy_prefix_migration( 60 ); } if ( function_exists( 'eslam_schedule_legacy_notifications_migration' ) ) { eslam_schedule_legacy_notifications_migration( 60 ); } if ( function_exists( 'eslam_schedule_geo_data_migration' ) ) { eslam_schedule_geo_data_migration( 90 ); } if ( function_exists( 'eslam_schedule_country_data_migration' ) ) { eslam_schedule_country_data_migration( 105 ); } if ( function_exists( 'eslam_schedule_unique_visits_data_migration' ) ) { eslam_schedule_unique_visits_data_migration( 120 ); } } ); /** * Run exactly one schema setup/repair component per background request. */ if ( ! function_exists( 'eslam_background_setup_components' ) ) { function eslam_background_setup_components() { return array( 'country' => 'eslam_ensure_author_country_stats_table', 'geo' => 'eslam_ensure_geo_cache_table', 'unique_visits' => 'eslam_ensure_author_unique_visits_table', 'notifications' => 'eslam_ensure_notifications_table', 'achievements' => 'eslam_achievements_ensure_tables', 'security_logs' => 'eslam_ensure_security_logs_table', ); } } if ( ! function_exists( 'eslam_background_setup_batch' ) ) { function eslam_background_setup_batch() { $lock_key = 'eslam_background_setup_lock'; if ( get_transient( $lock_key ) ) { eslam_schedule_background_setup( 60 ); return false; } set_transient( $lock_key, 1, 5 * MINUTE_IN_SECONDS ); try { $components = eslam_background_setup_components(); $state = get_option( ESLAM_BACKGROUND_SETUP_STATE_OPTION, array() ); $index = is_array( $state ) ? max( 0, intval( $state['index'] ?? 0 ) ) : 0; $keys = array_keys( $components ); if ( $index >= count( $keys ) ) { delete_option( ESLAM_BACKGROUND_SETUP_STATE_OPTION ); eslam_schedule_legacy_notifications_migration( 30 ); eslam_schedule_geo_data_migration( 30 ); eslam_schedule_country_data_migration( 45 ); eslam_schedule_unique_visits_data_migration( 60 ); return true; } $key = $keys[ $index ]; $callback = $components[ $key ]; if ( is_callable( $callback ) ) { /* * Existing tables that already carry the current schema version are * intentionally left alone. Force a schema repair only when that * component is actually stale; missing tables remain safe to create. */ $component_options = array( 'country' => ESLAM_AUTHOR_COUNTRY_TABLE_OPTION, 'geo' => ESLAM_GEO_CACHE_TABLE_OPTION, 'unique_visits' => ESLAM_UNIQUE_VISITS_TABLE_OPTION, 'notifications' => ESLAM_NOTIFICATIONS_TABLE_OPTION, 'achievements' => ESLAM_ACHIEVEMENTS_SCHEMA_OPTION, 'security_logs' => 'eslam_security_logs_table_version', ); $component_versions = array( 'country' => ESLAM_AUTHOR_COUNTRY_TABLE_VERSION, 'geo' => ESLAM_GEO_CACHE_TABLE_VERSION, 'unique_visits' => ESLAM_UNIQUE_VISITS_TABLE_VERSION, 'notifications' => ESLAM_NOTIFICATIONS_TABLE_VERSION, 'achievements' => ESLAM_ACHIEVEMENTS_SCHEMA_VERSION, 'security_logs' => ESLAM_SECURITY_LOGS_TABLE_VERSION, ); $option_name = isset( $component_options[ $key ] ) ? $component_options[ $key ] : ''; $version = isset( $component_versions[ $key ] ) ? $component_versions[ $key ] : ''; $force = ( $option_name !== '' && (string) get_option( $option_name, '' ) !== (string) $version ); $result = call_user_func( $callback, $force ); if ( false === $result ) { eslam_schedule_background_setup( 120 ); return false; } } update_option( ESLAM_BACKGROUND_SETUP_STATE_OPTION, array( 'index' => $index + 1 ), false ); eslam_schedule_background_setup( 15 ); return false; } catch ( Throwable $e ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( 'Author Metrics: background setup paused after error: ' . $e->getMessage() ); } eslam_schedule_background_setup( 120 ); return false; } finally { delete_transient( $lock_key ); } } } add_action( ESLAM_BACKGROUND_SETUP_HOOK, 'eslam_background_setup_batch' ); if ( ! function_exists( 'eslam_schedule_background_setup' ) ) { function eslam_schedule_background_setup( $delay = 20 ) { if ( ! wp_next_scheduled( ESLAM_BACKGROUND_SETUP_HOOK ) ) { wp_schedule_single_event( time() + max( 10, intval( $delay ) ), ESLAM_BACKGROUND_SETUP_HOOK ); } return true; } } if ( ! function_exists( 'eslam_admin_background_setup_fallback' ) ) { function eslam_admin_background_setup_fallback() { if ( ! defined( 'DISABLE_WP_CRON' ) || ! DISABLE_WP_CRON ) { return; } eslam_background_setup_batch(); } } add_action( 'admin_init', 'eslam_admin_background_setup_fallback', 3 ); /* * When WP-Cron is disabled, advance only one small migration batch per admin request. */ if ( ! function_exists( 'eslam_admin_data_migration_fallback' ) ) { function eslam_admin_data_migration_fallback() { if ( ! defined( 'DISABLE_WP_CRON' ) || ! DISABLE_WP_CRON ) { return; } $lock_key = 'eslam_admin_data_migration_fallback_lock'; if ( get_transient( $lock_key ) ) { return; } set_transient( $lock_key, 1, 90 ); try { if ( function_exists( 'eslam_migrate_legacy_notifications_batch' ) && get_option( ESLAM_NOTIFICATIONS_MIGRATION_OPTION, '' ) !== ESLAM_NOTIFICATIONS_MIGRATION_VERSION ) { eslam_migrate_legacy_notifications_batch( ESLAM_NOTIFICATIONS_MIGRATION_BATCH_SIZE ); return; } if ( function_exists( 'eslam_geo_data_migration_batch' ) && get_option( ESLAM_GEO_DATA_MIGRATION_OPTION, '' ) !== ESLAM_GEO_DATA_MIGRATION_VERSION ) { eslam_geo_data_migration_batch( ESLAM_GEO_DATA_MIGRATION_BATCH_SIZE ); return; } if ( function_exists( 'eslam_country_data_migration_batch' ) && get_option( ESLAM_COUNTRY_DATA_MIGRATION_OPTION, '' ) !== ESLAM_COUNTRY_DATA_MIGRATION_VERSION ) { eslam_country_data_migration_batch( ESLAM_COUNTRY_DATA_MIGRATION_BATCH_SIZE ); return; } if ( function_exists( 'eslam_unique_visits_data_migration_batch' ) && get_option( ESLAM_UNIQUE_VISITS_DATA_MIGRATION_OPTION, '' ) !== ESLAM_UNIQUE_VISITS_DATA_MIGRATION_VERSION ) { eslam_unique_visits_data_migration_batch( ESLAM_UNIQUE_VISITS_DATA_MIGRATION_BATCH_SIZE ); return; } if ( function_exists( 'eslam_migrate_legacy_prefixed_data' ) && get_option( 'eslam_db_prefix_migration_version', '' ) !== ESLAM_DB_PREFIX_MIGRATION_VERSION ) { eslam_migrate_legacy_prefixed_data(); } } catch ( Throwable $e ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( 'Author Metrics: admin migration fallback paused after error: ' . $e->getMessage() ); } } finally { delete_transient( $lock_key ); } } } add_action( 'admin_init', 'eslam_admin_data_migration_fallback', 4 ); /** * Migrate legacy data keys from the pre-2.1.1 two-letter prefix to the * collision-resistant plugin prefix without discarding existing site data. */ if ( ! function_exists( 'eslam_legacy_prefix_table_map' ) ) { function eslam_legacy_prefix_table_map() { return array( array( 'old' => 'author_country_stats_new', 'new' => 'eslam_author_country_stats' ), array( 'old' => 'author_stats_geo_cache', 'new' => 'eslam_author_geo_cache' ), array( 'old' => 'author_unique_visits', 'new' => 'eslam_author_unique_visits' ), array( 'old' => 'author_security_logs', 'new' => 'eslam_author_security_logs' ), array( 'old' => 'author_notifications', 'new' => 'eslam_author_notifications' ), ); } } if ( ! function_exists( 'eslam_legacy_prefix_common_columns' ) ) { function eslam_legacy_prefix_common_columns( $old_table, $new_table ) { global $wpdb; $old_columns = $wpdb->get_col( $wpdb->prepare( 'SHOW COLUMNS FROM %i', $old_table ), 0 ); $new_columns = $wpdb->get_col( $wpdb->prepare( 'SHOW COLUMNS FROM %i', $new_table ), 0 ); if ( ! is_array( $old_columns ) || ! is_array( $new_columns ) ) { return array(); } return array_values( array_filter( array_intersect( $old_columns, $new_columns ), static function( $column ) { return 'id' !== $column && preg_match( '/^[A-Za-z0-9_]+$/', (string) $column ); } ) ); } } if ( ! function_exists( 'eslam_migrate_legacy_prefix_table_batch' ) ) { /** * Merge one legacy table in bounded primary-key batches. * * RENAME TABLE is used when the destination does not exist, which is metadata-only * and avoids copying a large ledger. When both copies exist, rows are copied in * small INSERT IGNORE ... SELECT batches and the legacy table is retained. */ function eslam_migrate_legacy_prefix_table_batch( $table_index = 0, $last_id = 0, $limit = ESLAM_DB_PREFIX_MIGRATION_BATCH_SIZE ) { global $wpdb; $maps = eslam_legacy_prefix_table_map(); $table_index = max( 0, intval( $table_index ) ); $last_id = max( 0, intval( $last_id ) ); $limit = max( 10, min( 100, intval( $limit ) ) ); if ( $table_index >= count( $maps ) ) { return array( 'done' => true, 'table_index' => $table_index, 'last_id' => 0, 'changed' => false ); } $map = $maps[ $table_index ]; $old_table = $wpdb->prefix . $map['old']; $new_table = $wpdb->prefix . $map['new']; if ( ! eslam_table_exists( $old_table ) ) { return array( 'done' => false, 'table_index' => $table_index + 1, 'last_id' => 0, 'changed' => false ); } if ( ! eslam_table_exists( $new_table ) ) { $renamed = $wpdb->query( $wpdb->prepare( 'RENAME TABLE %i TO %i', $old_table, $new_table ) ); if ( false !== $renamed && eslam_table_exists( $new_table ) ) { return array( 'done' => false, 'table_index' => $table_index + 1, 'last_id' => 0, 'changed' => true ); } return array( 'done' => false, 'table_index' => $table_index + 1, 'last_id' => 0, 'changed' => false ); } $columns = eslam_legacy_prefix_common_columns( $old_table, $new_table ); if ( empty( $columns ) ) { return array( 'done' => false, 'table_index' => $table_index + 1, 'last_id' => 0, 'changed' => false ); } $column_sql = implode( ', ', array_fill( 0, count( $columns ), '%i' ) ); $ids = $wpdb->get_col( $wpdb->prepare( "SELECT id FROM %i WHERE id > %d ORDER BY id ASC LIMIT %d", $old_table, $last_id, $limit ) ); if ( empty( $ids ) ) { return array( 'done' => false, 'table_index' => $table_index + 1, 'last_id' => 0, 'changed' => false ); } $merge_sql = "INSERT IGNORE INTO %i (" . $column_sql . ") SELECT " . $column_sql . " FROM %i WHERE id > %d ORDER BY id ASC LIMIT %d"; $prepare_args = array_merge( array( $new_table ), $columns, array( $old_table ), $columns, array( $last_id, $limit ) ); $merged = $wpdb->query( $wpdb->prepare( $merge_sql, $prepare_args ) ); if ( false === $merged ) { return array( 'done' => false, 'error' => true, 'table_index' => $table_index, 'last_id' => $last_id, 'changed' => false, ); } $last_row_id = absint( end( $ids ) ); return array( 'done' => false, 'error' => false, 'table_index' => count( $ids ) >= $limit ? $table_index : $table_index + 1, 'last_id' => count( $ids ) >= $limit ? $last_row_id : 0, 'changed' => true, ); } } if ( ! function_exists( 'eslam_migrate_legacy_prefixed_tables' ) ) { /** * Compatibility wrapper retained for internal callers. It intentionally performs * only one bounded table batch and never executes an unbounded INSERT ... SELECT. */ function eslam_migrate_legacy_prefixed_tables() { $result = eslam_migrate_legacy_prefix_table_batch( 0, 0, ESLAM_DB_PREFIX_MIGRATION_BATCH_SIZE ); return ! empty( $result['done'] ); } } if ( ! defined( 'ESLAM_DB_PREFIX_MIGRATION_HOOK' ) ) { define( 'ESLAM_DB_PREFIX_MIGRATION_HOOK', 'eslam_db_prefix_migration_batch' ); } if ( ! defined( 'ESLAM_DB_PREFIX_MIGRATION_STATE_OPTION' ) ) { define( 'ESLAM_DB_PREFIX_MIGRATION_STATE_OPTION', 'eslam_db_prefix_migration_state' ); } if ( ! defined( 'ESLAM_DB_PREFIX_MIGRATION_BATCH_SIZE' ) ) { define( 'ESLAM_DB_PREFIX_MIGRATION_BATCH_SIZE', 50 ); } if ( ! function_exists( 'eslam_get_legacy_prefix_migration_state' ) ) { function eslam_get_legacy_prefix_migration_state() { $default = array( 'phase' => 'options', 'last_id' => 0, 'table_index' => 0, ); $state = get_option( ESLAM_DB_PREFIX_MIGRATION_STATE_OPTION, array() ); if ( ! is_array( $state ) ) { $state = array(); } return array_merge( $default, $state ); } } if ( ! function_exists( 'eslam_save_legacy_prefix_migration_state' ) ) { function eslam_save_legacy_prefix_migration_state( $state ) { update_option( ESLAM_DB_PREFIX_MIGRATION_STATE_OPTION, is_array( $state ) ? $state : array(), false ); } } if ( ! function_exists( 'eslam_schedule_legacy_prefix_migration' ) ) { function eslam_schedule_legacy_prefix_migration( $delay = 10 ) { if ( get_option( 'eslam_db_prefix_migration_version', '' ) === ESLAM_DB_PREFIX_MIGRATION_VERSION ) { return false; } if ( ! wp_next_scheduled( ESLAM_DB_PREFIX_MIGRATION_HOOK ) ) { wp_schedule_single_event( time() + max( 5, intval( $delay ) ), ESLAM_DB_PREFIX_MIGRATION_HOOK ); } return true; } } if ( ! function_exists( 'eslam_migrate_legacy_options_batch' ) ) { function eslam_migrate_legacy_options_batch( $last_id = 0, $limit = ESLAM_DB_PREFIX_MIGRATION_BATCH_SIZE ) { global $wpdb; $last_id = max( 0, intval( $last_id ) ); $limit = max( 1, min( 100, intval( $limit ) ) ); $like = $wpdb->esc_like( 'as_' ) . '%'; $rows = $wpdb->get_results( $wpdb->prepare( "SELECT option_id, option_name FROM %i USE INDEX (option_name) WHERE option_name LIKE %s AND option_id > %d ORDER BY option_id ASC LIMIT %d", $wpdb->options, $like, $last_id, $limit ), ARRAY_A ); if ( null === $rows ) { if ( ! empty( $wpdb->last_error ) ) { return array( 'ok' => false, 'has_more' => true, 'last_id' => $last_id, ); } $rows = array(); } $max_id = $last_id; foreach ( (array) $rows as $row ) { $option_id = isset( $row['option_id'] ) ? intval( $row['option_id'] ) : 0; if ( $option_id > $max_id ) { $max_id = $option_id; } $old_name = isset( $row['option_name'] ) ? (string) $row['option_name'] : ''; $new_name = 'eslam_' . substr( $old_name, 3 ); if ( '' === $new_name || $old_name === $new_name ) { continue; } $value = get_option( $old_name, null ); if ( null === $value ) { continue; } if ( 'eslam_tracking_protection_settings' === $new_name && is_array( $value ) ) { foreach ( array( 'count_adblock_non_monetized' => 'count_adblock_invalid', 'count_save_data_non_monetized' => 'count_save_data_invalid', 'count_privacy_non_monetized' => 'count_privacy_invalid', 'count_proxy_vpn_tor_non_monetized' => 'count_proxy_vpn_tor_invalid', ) as $legacy_key => $canonical_key ) { if ( array_key_exists( $legacy_key, $value ) && ! array_key_exists( $canonical_key, $value ) ) { $value[ $canonical_key ] = ! empty( $value[ $legacy_key ] ) ? 1 : 0; } unset( $value[ $legacy_key ] ); } } if ( null === get_option( $new_name, null ) ) { add_option( $new_name, $value, '', false ); } } return array( 'ok' => true, 'has_more' => count( $rows ) >= $limit, 'last_id' => $max_id, ); } } if ( ! function_exists( 'eslam_migrate_legacy_meta_batch' ) ) { function eslam_migrate_legacy_meta_batch( $meta_type, $last_id = 0, $limit = ESLAM_DB_PREFIX_MIGRATION_BATCH_SIZE ) { global $wpdb; $meta_type = $meta_type === 'postmeta' ? 'postmeta' : 'usermeta'; $table = $meta_type === 'postmeta' ? $wpdb->postmeta : $wpdb->usermeta; $id_column = $meta_type === 'postmeta' ? 'meta_id' : 'umeta_id'; $object_column = $meta_type === 'postmeta' ? 'post_id' : 'user_id'; $last_id = max( 0, intval( $last_id ) ); $limit = max( 1, min( 100, intval( $limit ) ) ); $like = $wpdb->esc_like( 'as_' ) . '%'; $rows = $wpdb->get_results( $wpdb->prepare( "SELECT %i, %i, meta_key, meta_value FROM %i USE INDEX (meta_key) WHERE meta_key LIKE %s AND %i > %d ORDER BY %i ASC LIMIT %d", $id_column, $object_column, $table, $like, $id_column, $last_id, $id_column, $limit ), ARRAY_A ); if ( null === $rows ) { if ( ! empty( $wpdb->last_error ) ) { return array( 'ok' => false, 'has_more' => true, 'last_id' => $last_id, ); } $rows = array(); } $max_id = $last_id; foreach ( (array) $rows as $row ) { $row_id = isset( $row[ $id_column ] ) ? intval( $row[ $id_column ] ) : 0; if ( $row_id > $max_id ) { $max_id = $row_id; } $old_key = isset( $row['meta_key'] ) ? (string) $row['meta_key'] : ''; $new_key = 'eslam_' . substr( $old_key, 3 ); $object_id = isset( $row[ $object_column ] ) ? absint( $row[ $object_column ] ) : 0; if ( '' === $new_key || $old_key === $new_key || $object_id <= 0 ) { continue; } // WordPress meta keys are limited to 255 bytes in the database schema. // Skip only the malformed key instead of aborting the complete migration. if ( strlen( $new_key ) > 255 ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( 'Author Metrics: skipped an overlong legacy meta key during prefix migration.' ); } continue; } $exists = $wpdb->get_var( $wpdb->prepare( "SELECT 1 FROM %i WHERE %i = %d AND meta_key = %s LIMIT 1", $table, $object_column, $object_id, $new_key ) ); if ( null !== $exists ) { continue; } $inserted = $wpdb->insert( $table, array( $object_column => $object_id, 'meta_key' => $new_key, 'meta_value' => isset( $row['meta_value'] ) ? $row['meta_value'] : '', ), array( '%d', '%s', '%s' ) ); // A bad individual legacy row must not prevent the remaining rows from // being migrated on the next batches. if ( false === $inserted && defined( 'WP_DEBUG' ) && WP_DEBUG && ! empty( $wpdb->last_error ) ) { error_log( 'Author Metrics: legacy meta migration row skipped: ' . $wpdb->last_error ); } } return array( 'ok' => true, 'has_more' => count( $rows ) >= $limit, 'last_id' => $max_id, ); } } if ( ! function_exists( 'eslam_migrate_legacy_prefixed_data' ) ) { /** * Progress the legacy prefix migration in bounded, resumable batches. * * Older releases used the short "as_" prefix. The previous migration loaded * every matching usermeta/postmeta row into a PHP array in one request, which * could exhaust memory on long-running production sites. The new migration * stores progress by primary-key cursor and processes only a small number of * rows per request. */ function eslam_migrate_legacy_prefixed_data() { if ( get_option( 'eslam_db_prefix_migration_version', '' ) === ESLAM_DB_PREFIX_MIGRATION_VERSION ) { delete_option( ESLAM_DB_PREFIX_MIGRATION_STATE_OPTION ); return true; } $lock_key = 'eslam_db_prefix_migration_lock'; if ( get_transient( $lock_key ) ) { eslam_schedule_legacy_prefix_migration( 30 ); return false; } set_transient( $lock_key, 1, 120 ); try { $state = eslam_get_legacy_prefix_migration_state(); $phase = sanitize_key( isset( $state['phase'] ) ? $state['phase'] : 'options' ); $last_id = max( 0, intval( $state['last_id'] ?? 0 ) ); $limit = max( 10, min( 100, intval( apply_filters( 'eslam_legacy_prefix_migration_batch_size', ESLAM_DB_PREFIX_MIGRATION_BATCH_SIZE ) ) ) ); if ( 'options' === $phase ) { $result = eslam_migrate_legacy_options_batch( $last_id, $limit ); if ( empty( $result['ok'] ) ) { eslam_schedule_legacy_prefix_migration( 60 ); return false; } if ( ! empty( $result['has_more'] ) ) { eslam_save_legacy_prefix_migration_state( array( 'phase' => 'options', 'last_id' => intval( $result['last_id'] ), ) ); eslam_schedule_legacy_prefix_migration( 15 ); return false; } eslam_save_legacy_prefix_migration_state( array( 'phase' => 'usermeta', 'last_id' => 0, ) ); eslam_schedule_legacy_prefix_migration( 15 ); return false; } if ( 'usermeta' === $phase ) { $result = eslam_migrate_legacy_meta_batch( 'usermeta', $last_id, $limit ); if ( empty( $result['ok'] ) ) { eslam_schedule_legacy_prefix_migration( 60 ); return false; } if ( ! empty( $result['has_more'] ) ) { eslam_save_legacy_prefix_migration_state( array( 'phase' => 'usermeta', 'last_id' => intval( $result['last_id'] ), ) ); eslam_schedule_legacy_prefix_migration( 15 ); return false; } eslam_save_legacy_prefix_migration_state( array( 'phase' => 'postmeta', 'last_id' => 0, ) ); eslam_schedule_legacy_prefix_migration( 15 ); return false; } if ( 'postmeta' === $phase ) { $result = eslam_migrate_legacy_meta_batch( 'postmeta', $last_id, $limit ); if ( empty( $result['ok'] ) ) { eslam_schedule_legacy_prefix_migration( 60 ); return false; } if ( ! empty( $result['has_more'] ) ) { eslam_save_legacy_prefix_migration_state( array( 'phase' => 'postmeta', 'last_id' => intval( $result['last_id'] ), ) ); eslam_schedule_legacy_prefix_migration( 15 ); return false; } foreach ( array( 'as_notifications_migrate_batch', 'as_cleanup_old_country_stats_daily', 'as_daily_prune_old_tracking_data' ) as $old_hook ) { wp_clear_scheduled_hook( $old_hook ); } /* * Legacy table merges and data normalization are separate from metadata * prefixing. They are intentionally continued in bounded table batches. */ eslam_save_legacy_prefix_migration_state( array( 'phase' => 'tables', 'table_index' => 0, 'last_id' => 0, ) ); eslam_schedule_legacy_prefix_migration( 15 ); return false; } if ( 'tables' === $phase ) { $table_index = max( 0, intval( $state['table_index'] ?? 0 ) ); $table_result = eslam_migrate_legacy_prefix_table_batch( $table_index, $last_id, $limit ); if ( ! empty( $table_result['error'] ) ) { eslam_schedule_legacy_prefix_migration( 120 ); return false; } if ( ! empty( $table_result['done'] ) ) { /* * These data migrations may already have completed before a legacy * table was merged. Reset their completion markers so newly merged * rows receive the same normalization as current rows. */ delete_option( ESLAM_GEO_DATA_MIGRATION_OPTION ); delete_option( ESLAM_GEO_DATA_MIGRATION_STATE_OPTION ); delete_option( ESLAM_COUNTRY_DATA_MIGRATION_OPTION ); delete_option( ESLAM_COUNTRY_DATA_MIGRATION_STATE_OPTION ); delete_option( ESLAM_UNIQUE_VISITS_DATA_MIGRATION_OPTION ); delete_option( ESLAM_UNIQUE_VISITS_DATA_MIGRATION_STATE_OPTION ); eslam_schedule_geo_data_migration( 30 ); eslam_schedule_country_data_migration( 45 ); eslam_schedule_unique_visits_data_migration( 60 ); update_option( 'eslam_db_prefix_migration_version', ESLAM_DB_PREFIX_MIGRATION_VERSION, false ); delete_option( ESLAM_DB_PREFIX_MIGRATION_STATE_OPTION ); return true; } eslam_save_legacy_prefix_migration_state( array( 'phase' => 'tables', 'table_index' => intval( $table_result['table_index'] ), 'last_id' => intval( $table_result['last_id'] ), ) ); eslam_schedule_legacy_prefix_migration( ! empty( $table_result['changed'] ) ? 15 : 20 ); return false; } // Unknown/corrupt state: restart safely from the first phase without // touching or deleting any legacy data. eslam_save_legacy_prefix_migration_state( array( 'phase' => 'options', 'last_id' => 0, 'table_index' => 0, ) ); eslam_schedule_legacy_prefix_migration( 15 ); return false; } catch ( \Throwable $e ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( 'Author Metrics: legacy prefix migration paused after error: ' . $e->getMessage() ); } eslam_schedule_legacy_prefix_migration( 60 ); return false; } finally { delete_transient( $lock_key ); } } } add_action( ESLAM_DB_PREFIX_MIGRATION_HOOK, 'eslam_migrate_legacy_prefixed_data' ); if ( ! function_exists( 'eslam_maybe_queue_legacy_prefix_migration' ) ) { function eslam_maybe_queue_legacy_prefix_migration() { eslam_schedule_legacy_prefix_migration( 10 ); } } add_action( 'init', 'eslam_maybe_queue_legacy_prefix_migration', 1 ); /* * If WP-Cron is disabled, allow one bounded batch to advance during admin * requests. It is intentionally never an unbounded migration. */ if ( ! function_exists( 'eslam_legacy_prefix_migration_admin_fallback' ) ) { function eslam_legacy_prefix_migration_admin_fallback() { if ( ! defined( 'DISABLE_WP_CRON' ) || ! DISABLE_WP_CRON ) { return; } if ( get_option( 'eslam_db_prefix_migration_version', '' ) === ESLAM_DB_PREFIX_MIGRATION_VERSION ) { return; } eslam_migrate_legacy_prefixed_data(); } } add_action( 'admin_init', 'eslam_legacy_prefix_migration_admin_fallback', 1 ); Comments on: Assassin’s Creed: Origins Free for PC With a Direct Link Download https://www.eslamgaming.com/2026/05/assassins-creed-origins-free-for-pc-with-a-direct-link-download.html Download Android And PC Games And Applications Mon, 11 May 2026 14:02:32 +0000 hourly 1 https://wordpress.org/?v=7.1.2