get_var( "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE 'il\\_%' AND option_name NOT IN ('flink_destino_tipos_migrated', 'flink_modo_frontend_removed')" ); if ( 0 === $count_old ) { // Nothing to migrate. Mark as done and exit fast. update_option( self::SENTINEL, '1', false ); return false; } // Run all phases in order. Each is idempotent on its own. self::migrate_options(); self::migrate_transients(); self::migrate_cron_events(); self::migrate_post_meta(); // Mark as completed. Use autoload=false so this sentinel doesn't // bloat the autoloaded options cache. update_option( self::SENTINEL, '1', false ); return true; } /** * Rename wp_options entries from 'il_*' to 'flink_*'. * Skips keys we explicitly want to leave alone (e.g. legacy migration * sentinels that were named with the old prefix). */ private static function migrate_options() { global $wpdb; // Direct UPDATE: rename the option_name column in one shot, for any // row starting with il_ that doesn't already have a flink_ twin // (defensive against partial previous runs). // // The CONCAT('flink_', SUBSTRING(option_name, 4)) strips the leading // "il_" (3 chars) and prepends "flink_". $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->options} o SET option_name = CONCAT(%s, SUBSTRING(option_name, 4)) WHERE option_name LIKE %s AND NOT EXISTS ( SELECT 1 FROM (SELECT option_name FROM {$wpdb->options}) o2 WHERE o2.option_name = CONCAT(%s, SUBSTRING(o.option_name, 4)) )", 'flink_', 'il\_%', 'flink_' ) ); // Sanity: clear the options cache so the next get_option() reads // the renamed rows. wp_cache_flush(); } /** * Rename transients. Transients are stored as two options each: * _transient_ → the payload * _transient_timeout_ → the expiry timestamp * * Plus site transients (multisite-safe): * _site_transient_ * _site_transient_timeout_ * * We rename all four variants. */ private static function migrate_transients() { global $wpdb; $prefixes = array( '_transient_', '_transient_timeout_', '_site_transient_', '_site_transient_timeout_', ); foreach ( $prefixes as $prefix ) { $like = $wpdb->esc_like( $prefix ) . 'il\_%'; $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->options} SET option_name = CONCAT(%s, 'flink_', SUBSTRING(option_name, %d)) WHERE option_name LIKE %s", $prefix, strlen( $prefix ) + 4, // +4 = skip "il_" (3 chars) + 1-based index $like ) ); } } /** * Migrate WP-Cron events. WordPress stores scheduled events in the * 'cron' option as a serialized array keyed by timestamp → hook name. * We unschedule the old hooks and reschedule under the new names. */ private static function migrate_cron_events() { // Hooks that were registered with the il_ prefix in old versions. $hook_map = array( ); foreach ( $hook_map as $old_hook => $new_hook ) { // Capture the args & timestamp of the current scheduled run (if any) // before unscheduling, so we can re-create it under the new name. $next = wp_next_scheduled( $old_hook ); if ( false !== $next ) { // Find out the recurrence (single vs hourly/daily/etc). // wp_get_scheduled_event was added in WP 5.1 — we use the // direct cron option for broader compat. $cron = _get_cron_array(); $recurrence = null; $args = array(); foreach ( (array) $cron as $ts => $hooks ) { if ( isset( $hooks[ $old_hook ] ) && is_array( $hooks[ $old_hook ] ) ) { foreach ( $hooks[ $old_hook ] as $key => $event ) { $recurrence = isset( $event['schedule'] ) ? $event['schedule'] : false; $args = isset( $event['args'] ) ? $event['args'] : array(); break 2; } } } // Unschedule the old hook entirely (all its occurrences). wp_clear_scheduled_hook( $old_hook, $args ); // Reschedule under the new name with the same recurrence. if ( $recurrence && false !== $recurrence ) { wp_schedule_event( $next, $recurrence, $new_hook, $args ); } else { wp_schedule_single_event( $next, $new_hook, $args ); } } } } /** * Rename post_meta keys with the legacy _il_ prefix to _flink_. * The leading underscore is the WP convention for "hidden" meta keys. * * Done in a single SQL UPDATE for efficiency. Post-meta tables can be * large (millions of rows on big sites) so we use a LIKE-based filter * to only touch the relevant rows. */ private static function migrate_post_meta() { global $wpdb; // _il_xxx → _flink_xxx (4 = strip "_il_" prefix, then prepend new) $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_key = CONCAT(%s, SUBSTRING(meta_key, 5)) WHERE meta_key LIKE %s", '_flink_', '_il\_%' ) ); } }