get_row( " SELECT COUNT(*) as cnt, SUM(LENGTH(option_value)) as total_bytes FROM {$wpdb->options} WHERE autoload = 'yes' ", ARRAY_A ); // 最大的 50 個 autoload 選項 $largest = $wpdb->get_results( " SELECT option_name, LENGTH(option_value) AS size_bytes, autoload FROM {$wpdb->options} WHERE autoload = 'yes' ORDER BY size_bytes DESC LIMIT 50 ", ARRAY_A ); // 可 defer 的候選項 $deferrable_candidates = array(); foreach ( $largest as $row ) { if ( (int) $row['size_bytes'] > self::SINGLE_OPTION_SIZE_THRESHOLD || in_array( $row['option_name'], self::KNOWN_DEFERRABLE_OPTIONS, true ) ) { $deferrable_candidates[] = $row; } } return array( 'total_count' => (int) $total['cnt'], 'total_bytes' => (int) $total['total_bytes'], 'total_mb' => round( $total['total_bytes'] / 1024 / 1024, 2 ), 'warning' => (int) $total['total_bytes'] > self::AUTOLOAD_WARNING_THRESHOLD_MB * 1024 * 1024, 'largest' => $largest, 'deferrable' => $deferrable_candidates, 'estimated_save_mb' => round( array_sum( array_column( $deferrable_candidates, 'size_bytes' ) ) / 1024 / 1024, 2 ), ); } /** * 執行 autoload 最佳化 * * @param array $options_to_defer 要設為 autoload=no 的選項名稱陣列 * 若為空,使用內建安全清單 * @param bool $dry_run */ public static function optimize_autoload( array $options_to_defer = array(), bool $dry_run = false ): array { global $wpdb; if ( empty( $options_to_defer ) ) { $options_to_defer = self::KNOWN_DEFERRABLE_OPTIONS; } $result = array( 'dry_run' => $dry_run, 'processed' => 0, 'saved_bytes' => 0, 'details' => array(), ); foreach ( $options_to_defer as $option_name ) { $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_id, LENGTH(option_value) AS size_bytes, autoload FROM {$wpdb->options} WHERE option_name = %s", $option_name ), ARRAY_A ); if ( ! $row ) { continue; } if ( $row['autoload'] === 'no' ) { continue; } if ( ! $dry_run ) { $wpdb->update( $wpdb->options, array( 'autoload' => 'no' ), array( 'option_name' => $option_name ), array( '%s' ), array( '%s' ) ); // 清除該選項的快取(下次讀取時會重建) wp_cache_delete( $option_name, 'options' ); wp_cache_delete( 'alloptions', 'options' ); } ++$result['processed']; $result['saved_bytes'] += (int) $row['size_bytes']; $result['details'][] = array( 'option_name' => $option_name, 'saved_bytes' => (int) $row['size_bytes'], ); } $result['saved_mb'] = round( $result['saved_bytes'] / 1024 / 1024, 2 ); return $result; } // ───────────────────────────────────────────────────────── // 選項群組重導向 // ───────────────────────────────────────────────────────── /** * 將一組選項重導向至專屬設定表 * * @param string $group_name 設定群組名 * @param array $option_keys 要管理的 option_name 清單 */ public static function register_settings_group( string $group_name, array $option_keys ): void { global $wpdb; // 受保護的選項不得重導向,否則會讓 WordPress 載入前讀取它的元件靜默失效 foreach ( array_intersect( $option_keys, self::PROTECTED_OPTIONS ) as $protected_key ) { _doing_it_wrong( __METHOD__, esc_html( "選項 {$protected_key} 由 WordPress 載入前的元件以原生 SQL 直讀 wp_options,重導向會使其靜默失效,已略過。" ), '1.0.2' ); } $option_keys = array_diff( $option_keys, self::PROTECTED_OPTIONS ); // 全部都被擋下就不必建表 if ( empty( $option_keys ) ) { return; } $table = $wpdb->prefix . TMDO_TABLE_PREFIX . 'settings_' . sanitize_key( $group_name ); // 建立設定專屬表 $charset = $wpdb->get_charset_collate(); $sql = "CREATE TABLE {$table} ( id INT NOT NULL AUTO_INCREMENT, setting_key VARCHAR(191) NOT NULL, setting_value LONGTEXT, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_key (setting_key) ) {$charset};"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( $sql ); // 註冊為管理中選項 foreach ( $option_keys as $key ) { self::$redirected_options[ $key ] = array( 'group' => $group_name, 'table' => $table, ); // 攔截讀取 add_filter( "pre_option_{$key}", function ( $value ) use ( $key, $table ) { return self::read_setting( $table, $key, $value ); }, 10, 1 ); // 攔截寫入:寫入 UAE 表,並回傳 $old_value 使 WP 跳過寫 wp_options add_filter( "pre_update_option_{$key}", function ( $value, $old_value ) use ( $key, $table ) { self::write_setting( $table, $key, $value ); // 回傳 $old_value 會讓 update_option() 判定「值未變動」進而跳過 wp_options 寫入 return $old_value; }, 10, 2 ); } } private static function read_setting( string $table, string $key, $default ) { global $wpdb; $value = $wpdb->get_var( $wpdb->prepare( "SELECT setting_value FROM `{$table}` WHERE setting_key = %s", $key ) ); if ( $value === null ) { return $default; } // v2.13.3: object-injection-safe unserialize (fixes L-DESER-1). $decoded = TMDO_Safe_Unserialize::run( $value ); return $decoded; } private static function write_setting( string $table, string $key, $value ): void { global $wpdb; $wpdb->replace( $table, array( 'setting_key' => $key, 'setting_value' => maybe_serialize( $value ), ), array( '%s', '%s' ) ); } public static function get_redirected_options(): array { return self::$redirected_options; } }