get_meta_keys(); if ( empty( $meta_keys ) ) { return 0; } $placeholders = implode( ',', array_fill( 0, count( $meta_keys ), '%s' ) ); // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- $placeholders from array_fill; $wpdb->postmeta is a core property. return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key IN ({$placeholders})", // phpcs:ignore WPDO.AntiEAV.no-direct-postmeta-select -- Warm migration: postmeta → Zone B path. ...$meta_keys ) ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber } /** * Migrates one batch of warm meta rows. * * @param int $offset Starting row offset. * @return int Number of rows processed. */ protected function migrate_batch( int $offset ): int { global $wpdb; $meta_keys = $this->get_meta_keys(); if ( empty( $meta_keys ) ) { return 0; } $registry = TMDO_Schema_Registry::instance(); $placeholders = implode( ',', array_fill( 0, count( $meta_keys ), '%s' ) ); $args = array_merge( $meta_keys, array( self::BATCH_SIZE, $offset ) ); // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- $placeholders from array_fill; $wpdb->postmeta is a core property. $rows = $wpdb->get_results( $wpdb->prepare( "SELECT post_id, meta_key, meta_value FROM {$wpdb->postmeta} WHERE meta_key IN ({$placeholders}) ORDER BY meta_id ASC LIMIT %d OFFSET %d", ...$args ), ARRAY_A ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber if ( empty( $rows ) ) { return 0; } foreach ( $rows as $row ) { $field = $registry->get_warm_field( $row['meta_key'] ); $ttl = $field['ttl'] ?? null; TMDO_Zone_Warm::set( (int) $row['post_id'], $row['meta_key'], $row['meta_value'], $ttl ); } return count( $rows ); } /** * Verifies that the warm table row count is at least as large as the source. * * @return bool True if verification passes. */ public function verify_counts(): bool { global $wpdb; $source = $this->count_source(); $table = TMDO_Zone_Warm::table(); $target = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from TMDO_Zone_Warm::table() return $target >= $source; } // ── Private helpers ─────────────────────────────────────────────────── /** * Get all registered warm meta_keys. * * @return array List of meta key strings. */ private function get_meta_keys(): array { $fields = TMDO_Schema_Registry::instance()->get_zone_fields( 'warm' ); return array_column( $fields, 'meta_key' ); } }