refactor(2meet-data-optimizer): remove HPCT legacy from core
- Feature flags keep only the live AddOn modules (wc_orders, latepoint) as ADDON_MODULES / addon_modules(); drop map_hpct_status() and the 7 HivePress HPCT modules whose interceptors were removed in PR-014 - Remove HPCT detection/notice (Compatibility, core), the admin HPCT import tab and status row, CLI import-hpct, the 7 advisor rules for dead modules, and the removed interceptors from COEXIST_WHITELIST - Verify: NoHpctLegacyTest guard; core unit 635 / integration 449, HP 145/49, WC 10/22 OK; all sites have every module idle and never imported HPCT - PR: PR-020c
This commit is contained in:
@@ -783,11 +783,6 @@ class TMDO_Admin {
|
||||
$tab = 'dashboard';
|
||||
}
|
||||
|
||||
// Show HPCT Import tab only when HPCT is detected.
|
||||
if ( TMDO_Compatibility::should_show_hpct_notice() ) {
|
||||
$tabs['hpct-import'] = __( 'HPCT 匯入', '2meet-data-optimizer' );
|
||||
}
|
||||
|
||||
// v2.8.1: red-dot badge on tabs that need operator attention.
|
||||
$needs_dot = array();
|
||||
if ( class_exists( 'TMDO_Migration_Orchestrator' ) ) {
|
||||
@@ -811,7 +806,7 @@ class TMDO_Admin {
|
||||
),
|
||||
'wizards' => array(
|
||||
'label' => esc_html__( '🧙 遷移精靈', '2meet-data-optimizer' ),
|
||||
'tabs' => array( 'migration-wizard', 'post-migration-wizard', 'hpct-import' ),
|
||||
'tabs' => array( 'migration-wizard', 'post-migration-wizard' ),
|
||||
),
|
||||
'stress' => array(
|
||||
'label' => esc_html__( '⚡ 壓力測試', '2meet-data-optimizer' ),
|
||||
@@ -939,9 +934,6 @@ class TMDO_Admin {
|
||||
case 'rest-api':
|
||||
self::render_rest_api();
|
||||
break;
|
||||
case 'hpct-import':
|
||||
self::render_hpct_import();
|
||||
break;
|
||||
case 'hivepress':
|
||||
if ( class_exists( 'TMDO_Admin_HivePress' ) ) {
|
||||
TMDO_Admin_HivePress::render();
|
||||
@@ -1121,20 +1113,6 @@ class TMDO_Admin {
|
||||
<th><?php esc_html_e( 'HivePress', '2meet-data-optimizer' ); ?></th>
|
||||
<td><?php echo wp_kses_post( $compat['hivepress'] ? '<span class="wpdo-badge wpdo-badge-ok">Active</span>' : '<span class="wpdo-badge">N/A</span>' ); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'HP Custom Tables', '2meet-data-optimizer' ); ?></th>
|
||||
<td>
|
||||
<?php
|
||||
if ( $compat['hpct_active'] && $compat['hpct_imported'] ) {
|
||||
echo '<span class="wpdo-badge wpdo-badge-ok">Imported</span>';
|
||||
} elseif ( $compat['hpct_active'] ) {
|
||||
echo '<span class="wpdo-badge wpdo-badge-warn">Needs Import</span>';
|
||||
} else {
|
||||
echo '<span class="wpdo-badge">N/A</span>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Object Cache', '2meet-data-optimizer' ); ?></th>
|
||||
<td>
|
||||
@@ -1169,11 +1147,11 @@ class TMDO_Admin {
|
||||
<h2><?php esc_html_e( '模組狀態', '2meet-data-optimizer' ); ?></h2>
|
||||
<div class="wpdo-grid">
|
||||
<div>
|
||||
<h3>HPCT Modules</h3>
|
||||
<h3>AddOn Modules</h3>
|
||||
<table class="widefat striped">
|
||||
<thead><tr><th><?php esc_html_e( '模組', '2meet-data-optimizer' ); ?></th><th><?php esc_html_e( '狀態', '2meet-data-optimizer' ); ?></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ( TMDO_Feature_Flags::hpct_modules() as $module => $state ) : ?>
|
||||
<?php foreach ( TMDO_Feature_Flags::addon_modules() as $module => $state ) : ?>
|
||||
<tr>
|
||||
<td><code><?php echo esc_html( $module ); ?></code></td>
|
||||
<td><span class="wpdo-state wpdo-state-<?php echo esc_attr( $state ); ?>"><?php echo esc_html( $state ); ?></span></td>
|
||||
@@ -2717,79 +2695,6 @@ wpdo.getListings({ per_page: 3 }).then(r => console.log(r));'
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* HPCT Import tab: preview and execute import.
|
||||
*/
|
||||
private static function render_hpct_import(): void {
|
||||
// HPCT is a HivePress-family plugin; the importer ships in that AddOn.
|
||||
if ( ! class_exists( 'TMDO_HPCT_Import' ) ) {
|
||||
echo '<div class="notice notice-warning"><p>'
|
||||
. esc_html__( 'HPCT 匯入需要啟用 2meet-data-optimizer-hivepress-addon。', '2meet-data-optimizer' )
|
||||
. '</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle import action.
|
||||
if ( isset( $_POST['wpdo_run_import'] ) && check_admin_referer( 'wpdo_hpct_import' ) ) {
|
||||
$result = TMDO_HPCT_Import::run();
|
||||
if ( is_wp_error( $result ) ) {
|
||||
echo '<div class="notice notice-error"><p>' . esc_html( $result->get_error_message() ) . '</p></div>';
|
||||
} else {
|
||||
echo '<div class="notice notice-success"><p>' . esc_html__( 'HPCT 匯入成功!建議停用 HP Custom Tables 外掛。', '2meet-data-optimizer' ) . '</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
$can_import = TMDO_HPCT_Import::can_import();
|
||||
$is_imported = TMDO_HPCT_Import::is_imported();
|
||||
|
||||
?>
|
||||
<h2><?php esc_html_e( 'HP Custom Tables 匯入', '2meet-data-optimizer' ); ?></h2>
|
||||
|
||||
<?php if ( $is_imported ) : ?>
|
||||
<div class="notice notice-success inline">
|
||||
<p><?php esc_html_e( 'HPCT 設定已匯入完成。如果 HP Custom Tables 外掛仍然啟用,建議停用它。', '2meet-data-optimizer' ); ?></p>
|
||||
</div>
|
||||
<?php elseif ( $can_import ) : ?>
|
||||
<p class="description"><?php esc_html_e( '偵測到 HP Custom Tables 外掛。匯入會將 HPCT 的模組狀態和遷移記錄複製到 WPDO,然後由 WPDO 接管所有攔截器。', '2meet-data-optimizer' ); ?></p>
|
||||
|
||||
<?php
|
||||
$preview_data = TMDO_HPCT_Import::preview();
|
||||
if ( ! empty( $preview_data['modules'] ) ) :
|
||||
$preview = $preview_data['modules'];
|
||||
?>
|
||||
<h3><?php esc_html_e( '匯入預覽', '2meet-data-optimizer' ); ?></h3>
|
||||
<table class="widefat striped wpdo-table--narrow">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'HPCT 模組', '2meet-data-optimizer' ); ?></th>
|
||||
<th><?php esc_html_e( 'HPCT 狀態', '2meet-data-optimizer' ); ?></th>
|
||||
<th><?php esc_html_e( 'WPDO 狀態', '2meet-data-optimizer' ); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ( $preview as $item ) : ?>
|
||||
<tr>
|
||||
<td><code><?php echo esc_html( $item['module'] ); ?></code></td>
|
||||
<td><?php echo esc_html( $item['hpct_status'] ); ?></td>
|
||||
<td><span class="wpdo-state wpdo-state-<?php echo esc_attr( $item['wpdo_state'] ); ?>"><?php echo esc_html( $item['wpdo_state'] ); ?></span></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<form method="post" class="wpdo-mt-3">
|
||||
<?php wp_nonce_field( 'wpdo_hpct_import' ); ?>
|
||||
<?php submit_button( __( '執行匯入', '2meet-data-optimizer' ), 'primary', 'wpdo_run_import', false ); ?>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php else : ?>
|
||||
<div class="notice notice-info inline">
|
||||
<p><?php esc_html_e( '未偵測到 HP Custom Tables 外掛,或已完成匯入。', '2meet-data-optimizer' ); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
}
|
||||
|
||||
// ── AJAX handler ──────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
@@ -3740,7 +3645,7 @@ wpdo.getListings({ per_page: 3 }).then(r => console.log(r));'
|
||||
<?php
|
||||
$blacklist = class_exists( 'TMDO_FSM_Automator' ) ? TMDO_FSM_Automator::blacklist() : array();
|
||||
if ( class_exists( 'TMDO_Feature_Flags' ) ) {
|
||||
$all = array_merge( TMDO_Feature_Flags::HPCT_MODULES, TMDO_Feature_Flags::ZONE_MODULES );
|
||||
$all = array_merge( TMDO_Feature_Flags::ADDON_MODULES, TMDO_Feature_Flags::ZONE_MODULES );
|
||||
echo '<div class="wpdo-details-section">';
|
||||
foreach ( $all as $m ) {
|
||||
printf(
|
||||
|
||||
@@ -107,7 +107,7 @@ class TMDO_Help_Tabs {
|
||||
private static function content_dashboard(): string {
|
||||
return '<p>' . esc_html__( '儀表板顯示:', '2meet-data-optimizer' ) . '</p>'
|
||||
. '<ul class="wpdo-list-disc">'
|
||||
. '<li>' . esc_html__( 'System Overview — DB 引擎、HivePress、HPCT、Object Cache 是否啟用', '2meet-data-optimizer' ) . '</li>'
|
||||
. '<li>' . esc_html__( 'System Overview — DB 引擎、HivePress、Object Cache 是否啟用', '2meet-data-optimizer' ) . '</li>'
|
||||
. '<li>' . esc_html__( 'Zone 行數統計 — Hot/Warm/Cold/Archive 各自累積多少資料', '2meet-data-optimizer' ) . '</li>'
|
||||
. '<li>' . esc_html__( 'Module 狀態 — 每個 module 在 7-state FSM 哪一格', '2meet-data-optimizer' ) . '</li>'
|
||||
. '<li>' . esc_html__( 'Warm zone live view — 哪些 view counts / TTL 進來、24h 快過期數', '2meet-data-optimizer' ) . '</li>'
|
||||
@@ -173,7 +173,6 @@ class TMDO_Help_Tabs {
|
||||
. '<p>' . esc_html__( '常見原因:', '2meet-data-optimizer' ) . '</p>'
|
||||
. '<ul class="wpdo-list-disc">'
|
||||
. '<li>' . esc_html__( 'Hook Bus 啟用(wpdo_hook_bus_enabled = 1)+ legacy interceptors 還沒卸載', '2meet-data-optimizer' ) . '</li>'
|
||||
. '<li>' . esc_html__( 'HPCT (HP Custom Tables) plugin 還沒移除 — 與 WPDO 同時攔截', '2meet-data-optimizer' ) . '</li>'
|
||||
. '</ul>'
|
||||
. '<p>' . esc_html__( '解法:先看 conflict-scan 詳情,必要時用 wp wpdo bridge-set off 暫停 Hook Bus 直到清理完。', '2meet-data-optimizer' ) . '</p>';
|
||||
}
|
||||
|
||||
+4
-66
@@ -42,7 +42,6 @@ class TMDO_CLI {
|
||||
|
||||
$compat = TMDO_Compatibility::check();
|
||||
WP_CLI::log( 'HivePress: ' . ( $compat['hivepress'] ? 'Active' : 'Not found' ) );
|
||||
WP_CLI::log( 'HP Custom Tables: ' . self::format_hpct_status( $compat ) );
|
||||
WP_CLI::log( 'Object Cache: ' . ( wp_using_ext_object_cache() ? 'External' : 'Built-in' ) );
|
||||
WP_CLI::log( '' );
|
||||
|
||||
@@ -57,10 +56,10 @@ class TMDO_CLI {
|
||||
|
||||
WP_CLI::log( 'Module States:' );
|
||||
|
||||
$hpct_flags = TMDO_Feature_Flags::hpct_modules();
|
||||
if ( ! empty( $hpct_flags ) ) {
|
||||
WP_CLI::log( ' [HPCT Modules]' );
|
||||
foreach ( $hpct_flags as $module => $state ) {
|
||||
$addon_flags = TMDO_Feature_Flags::addon_modules();
|
||||
if ( ! empty( $addon_flags ) ) {
|
||||
WP_CLI::log( ' [AddOn Modules]' );
|
||||
foreach ( $addon_flags as $module => $state ) {
|
||||
WP_CLI::log( " {$module}: {$state}" );
|
||||
}
|
||||
}
|
||||
@@ -668,51 +667,6 @@ class TMDO_CLI {
|
||||
WP_CLI::success( "Module {$module} disabled (idle)." );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import settings from HP Custom Tables.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp wpdo import-hpct
|
||||
*
|
||||
* @param array $args Positional arguments.
|
||||
* @param array $assoc_args Associative arguments.
|
||||
* @return void
|
||||
*
|
||||
* @subcommand import-hpct
|
||||
*/
|
||||
public function import_hpct( $args, $assoc_args ): void { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
// HPCT is a HivePress-family plugin; the importer ships in that AddOn.
|
||||
if ( ! class_exists( 'TMDO_HPCT_Import' ) ) {
|
||||
WP_CLI::error( 'import-hpct needs 2meet-data-optimizer-hivepress-addon to be active.' );
|
||||
}
|
||||
|
||||
if ( TMDO_HPCT_Import::is_imported() ) {
|
||||
WP_CLI::warning( 'HPCT settings have already been imported.' );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! TMDO_HPCT_Import::can_import() ) {
|
||||
WP_CLI::error( 'HP Custom Tables plugin not detected or already imported.' );
|
||||
}
|
||||
|
||||
// Show preview.
|
||||
$preview_data = TMDO_HPCT_Import::preview();
|
||||
if ( ! empty( $preview_data['modules'] ) ) {
|
||||
WP_CLI::log( 'Import preview:' );
|
||||
WP_CLI\Utils\format_items( 'table', $preview_data['modules'], array( 'module', 'hpct_status', 'wpdo_state' ) );
|
||||
}
|
||||
|
||||
WP_CLI::confirm( 'Proceed with import?' );
|
||||
|
||||
$result = TMDO_HPCT_Import::run();
|
||||
if ( is_wp_error( $result ) ) {
|
||||
WP_CLI::error( $result->get_error_message() );
|
||||
}
|
||||
|
||||
WP_CLI::success( 'HPCT settings imported successfully. You can now deactivate HP Custom Tables.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run performance benchmark.
|
||||
*
|
||||
@@ -1430,22 +1384,6 @@ class TMDO_CLI {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the HPCT status string for display.
|
||||
*
|
||||
* @param array $compat Compatibility check result array.
|
||||
* @return string Human-readable HPCT status.
|
||||
*/
|
||||
private static function format_hpct_status( array $compat ): string {
|
||||
if ( $compat['hpct_active'] && $compat['hpct_imported'] ) {
|
||||
return 'Active (Imported)';
|
||||
}
|
||||
if ( $compat['hpct_active'] ) {
|
||||
return 'Active (Not imported — run `wp wpdo import-hpct`)';
|
||||
}
|
||||
return 'Not found';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the appropriate migration instance for a module name.
|
||||
*
|
||||
|
||||
@@ -284,7 +284,6 @@ class TMDO_Module_Detector {
|
||||
$active = match ( $plugin ) {
|
||||
'hivepress' => TMDO_Compatibility::is_hivepress_active(),
|
||||
'woocommerce' => TMDO_Compatibility::is_woocommerce_active(),
|
||||
'hpct' => TMDO_Compatibility::is_hpct_active(),
|
||||
'latepoint' => TMDO_Compatibility::is_latepoint_active(),
|
||||
default => false,
|
||||
};
|
||||
|
||||
@@ -29,52 +29,10 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
class TMDO_Module_Rules {
|
||||
|
||||
/**
|
||||
* Default rules for the 15 known modules. Use `get()` to apply filter.
|
||||
* Default rules for the 8 known modules. Use `get()` to apply filter.
|
||||
*/
|
||||
private const DEFAULT_RULES = array(
|
||||
// ─── HPCT modules(HivePress + HPCT)────────────────────────────
|
||||
'reviews' => array(
|
||||
'compat_required' => array( 'hivepress' ),
|
||||
'post_type_required' => 'hp_review',
|
||||
'min_post_count' => 100,
|
||||
'description' => '若使用 HivePress reviews 且累積評論 ≥ 100 條,啟用後可顯著降低 listing 查詢的 JOIN 成本。',
|
||||
),
|
||||
'messages' => array(
|
||||
'compat_required' => array( 'hivepress' ),
|
||||
'post_type_required' => 'hp_message_thread',
|
||||
'min_post_count' => 50,
|
||||
'description' => 'HivePress messages 累積對話數 ≥ 50 時,啟用 module 可加速 inbox / unread count 查詢。',
|
||||
),
|
||||
'favorites' => array(
|
||||
'compat_required' => array( 'hivepress' ),
|
||||
'post_type_required' => 'hp_favorite',
|
||||
'min_post_count' => 200,
|
||||
'description' => '使用者 favorite 累積 ≥ 200 條時,啟用 module 把 favorite meta 從 wp_postmeta 搬出。',
|
||||
),
|
||||
'memberships' => array(
|
||||
'compat_required' => array( 'hivepress' ),
|
||||
'post_type_required' => 'hp_membership',
|
||||
'min_post_count' => 50,
|
||||
'description' => 'HivePress memberships 啟用且 ≥ 50 條訂閱時,membership 過期檢查會更快。',
|
||||
),
|
||||
'statistics' => array(
|
||||
'compat_required' => array( 'hivepress' ),
|
||||
'post_type_required' => 'hp_listing',
|
||||
'min_post_count' => 500,
|
||||
'description' => 'Listing ≥ 500 條時,view_count / favorite_count 等高頻統計搬到 stats module 可大幅減少 wp_postmeta 寫入。',
|
||||
),
|
||||
'requests' => array(
|
||||
'compat_required' => array( 'hivepress' ),
|
||||
'post_type_required' => 'hp_request',
|
||||
'min_post_count' => 50,
|
||||
'description' => 'HivePress requests / quotes 累積 ≥ 50 條時建議啟用。',
|
||||
),
|
||||
'listing_meta' => array(
|
||||
'compat_required' => array( 'hivepress' ),
|
||||
'post_type_required' => 'hp_listing',
|
||||
'min_post_count' => 100,
|
||||
'description' => '所有 listing meta 集中管理;listing ≥ 100 時可省下大量 postmeta JOIN。',
|
||||
),
|
||||
// ─── AddOn modules ──────────────────────────────────────────────
|
||||
'wc_orders' => array(
|
||||
'compat_required' => array( 'woocommerce' ),
|
||||
'post_type_required' => 'shop_order',
|
||||
|
||||
@@ -37,57 +37,23 @@ class TMDO_Compatibility {
|
||||
}
|
||||
|
||||
self::$detected = array(
|
||||
'hpct_active' => self::is_hpct_active(),
|
||||
'hpct_imported' => self::is_hpct_imported(),
|
||||
'hivepress' => self::is_hivepress_active(),
|
||||
'woocommerce' => self::is_woocommerce_active(),
|
||||
'hivepress' => self::is_hivepress_active(),
|
||||
'woocommerce' => self::is_woocommerce_active(),
|
||||
);
|
||||
|
||||
return self::$detected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can WPDO register its HPCT-inherited module interceptors?
|
||||
*
|
||||
* True when:
|
||||
* - hp-custom-tables is NOT active, OR
|
||||
* - hp-custom-tables IS active but already imported into WPDO.
|
||||
*/
|
||||
public static function can_register_hpct_hooks(): bool {
|
||||
$compat = self::check();
|
||||
|
||||
// HPCT not active → WPDO can freely register.
|
||||
if ( ! $compat['hpct_active'] ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// HPCT is active but already imported → WPDO takes over.
|
||||
if ( $compat['hpct_imported'] ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// HPCT is active and NOT imported → defer to HPCT.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can WPDO register zone-specific interceptors (hot/warm/cold/archive)?
|
||||
*
|
||||
* Zone interceptors are always safe to register — they operate on
|
||||
* WPDO's own tables and do not conflict with HPCT.
|
||||
* WPDO's own tables.
|
||||
*/
|
||||
public static function can_register_zone_hooks(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should WPDO show an admin notice about HPCT import?
|
||||
*/
|
||||
public static function should_show_hpct_notice(): bool {
|
||||
$compat = self::check();
|
||||
return $compat['hpct_active'] && ! $compat['hpct_imported'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Is HivePress core active?
|
||||
*/
|
||||
@@ -113,23 +79,4 @@ class TMDO_Compatibility {
|
||||
|| defined( 'LATEPOINT_VERSION' )
|
||||
|| function_exists( 'latepoint_get_settings' );
|
||||
}
|
||||
|
||||
// ── Detection Methods ────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Is HP Custom Tables plugin active (loaded)?
|
||||
*
|
||||
* Checks for the HPCT_Loader class, which is always loaded
|
||||
* when hp-custom-tables is active.
|
||||
*/
|
||||
public static function is_hpct_active(): bool {
|
||||
return class_exists( 'HPCT_Loader' ) || defined( 'HPCT_VERSION' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Has HPCT been imported into WPDO?
|
||||
*/
|
||||
private static function is_hpct_imported(): bool {
|
||||
return (bool) get_option( 'wpdo_hpct_imported', false );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +95,6 @@ class TMDO_Core {
|
||||
// Cache Layer hooks (Zone C prefetch + save_post warming).
|
||||
TMDO_Cache_Layer::register_hooks();
|
||||
|
||||
// Register HPCT-inherited module interceptors if allowed.
|
||||
if ( TMDO_Compatibility::can_register_hpct_hooks() ) {
|
||||
$this->register_hpct_interceptors();
|
||||
}
|
||||
|
||||
// Zone interceptors are always safe to register.
|
||||
if ( TMDO_Compatibility::can_register_zone_hooks() ) {
|
||||
$this->register_zone_hooks();
|
||||
@@ -130,11 +125,6 @@ class TMDO_Core {
|
||||
// Schedule cron events.
|
||||
$this->schedule_cron();
|
||||
|
||||
// Admin notice for HPCT import.
|
||||
if ( is_admin() && TMDO_Compatibility::should_show_hpct_notice() ) {
|
||||
add_action( 'admin_notices', array( $this, 'render_hpct_notice' ) );
|
||||
}
|
||||
|
||||
// v2.6.6: async entity backfill cron handler.
|
||||
add_action( 'wpdo_entity_backfill_batch', array( $this, 'run_entity_backfill_batch' ), 10, 2 );
|
||||
|
||||
@@ -248,16 +238,6 @@ class TMDO_Core {
|
||||
do_action( 'wpdo_register_entity_fields', TMDO_Entity_Registry::class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register HPCT-inherited module interceptors and query interceptors.
|
||||
*/
|
||||
private function register_hpct_interceptors(): void {
|
||||
// 7 HPCT-era meta interceptors 已搬到 2meet-data-optimizer-hivepress-addon。
|
||||
// 1 LatePoint interceptor 已搬到 2meet-data-optimizer-latepoint-addon。
|
||||
// 5 query interceptors 已搬到 2meet-data-optimizer-hivepress-addon。
|
||||
// 此方法保留為空殼以保持 ABI 相容;AddOn 各自於自身 bootstrap 內 register_hooks。
|
||||
}
|
||||
|
||||
/**
|
||||
* Register zone-specific hooks.
|
||||
*/
|
||||
@@ -555,27 +535,6 @@ class TMDO_Core {
|
||||
TMDO_Zone_Archive::sweep();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render admin notice when hp-custom-tables is detected but not imported.
|
||||
*/
|
||||
public function render_hpct_notice(): void {
|
||||
$import_url = admin_url( 'tools.php?page=wp-data-optimizer&tab=hpct-import' );
|
||||
?>
|
||||
<div class="notice notice-warning is-dismissible">
|
||||
<p>
|
||||
<strong>WP Data Optimizer:</strong>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %s: URL to import page */
|
||||
esc_html__( 'HP Custom Tables 外掛已偵測到。請前往 %s 匯入其設定,然後停用 HP Custom Tables。', '2meet-data-optimizer' ),
|
||||
'<a href="' . esc_url( $import_url ) . '">' . esc_html__( 'HPCT 匯入頁面', '2meet-data-optimizer' ) . '</a>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* WP Cron handler: process one backfill batch for an entity/group pair.
|
||||
*
|
||||
|
||||
@@ -27,17 +27,9 @@ class TMDO_Feature_Flags {
|
||||
private const OPTION_KEY = 'wpdo_features';
|
||||
|
||||
/**
|
||||
* All known HPCT-inherited modules.
|
||||
* These modules operate on existing hpct_* tables.
|
||||
* Modules driven by AddOn interceptors (WooCommerce commissions, LatePoint).
|
||||
*/
|
||||
public const HPCT_MODULES = array(
|
||||
'reviews',
|
||||
'messages',
|
||||
'favorites',
|
||||
'memberships',
|
||||
'statistics',
|
||||
'requests',
|
||||
'listing_meta',
|
||||
public const ADDON_MODULES = array(
|
||||
'wc_orders',
|
||||
'latepoint',
|
||||
);
|
||||
@@ -300,7 +292,7 @@ class TMDO_Feature_Flags {
|
||||
$saved = array();
|
||||
}
|
||||
|
||||
$all_modules = array_merge( self::HPCT_MODULES, self::ZONE_MODULES );
|
||||
$all_modules = array_merge( self::ADDON_MODULES, self::ZONE_MODULES );
|
||||
$defaults = array_fill_keys( $all_modules, 'idle' );
|
||||
|
||||
self::$cache = array_merge( $defaults, $saved );
|
||||
@@ -308,13 +300,13 @@ class TMDO_Feature_Flags {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return only HPCT-inherited module states.
|
||||
* Return only AddOn module states.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function hpct_modules(): array {
|
||||
public static function addon_modules(): array {
|
||||
$all = self::all();
|
||||
return array_intersect_key( $all, array_flip( self::HPCT_MODULES ) );
|
||||
return array_intersect_key( $all, array_flip( self::ADDON_MODULES ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,23 +318,4 @@ class TMDO_Feature_Flags {
|
||||
$all = self::all();
|
||||
return array_intersect_key( $all, array_flip( self::ZONE_MODULES ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an HPCT feature flag status to a WPDO state.
|
||||
*
|
||||
* Used during HPCT import to translate HPCT's 4-state model
|
||||
* to WPDO's 7-state model.
|
||||
*
|
||||
* @param string $hpct_status HPCT status (disabled/migrating/verified/enabled).
|
||||
* @return string WPDO state.
|
||||
*/
|
||||
public static function map_hpct_status( string $hpct_status ): string {
|
||||
return match ( $hpct_status ) {
|
||||
'disabled' => 'idle',
|
||||
'migrating' => 'backfill',
|
||||
'verified' => 'cutover',
|
||||
'enabled' => 'complete',
|
||||
default => 'idle',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,13 +98,6 @@ final class TMDO_Hook_Bus_Bridge {
|
||||
* @var string[]
|
||||
*/
|
||||
private const COEXIST_WHITELIST = array(
|
||||
'TMDO_Reviews_Interceptor',
|
||||
'TMDO_Messages_Interceptor',
|
||||
'TMDO_Favorites_Interceptor',
|
||||
'TMDO_Memberships_Interceptor',
|
||||
'TMDO_Statistics_Interceptor',
|
||||
'TMDO_Requests_Interceptor',
|
||||
'TMDO_Listing_Meta_Interceptor',
|
||||
'TMDO_WC_Orders_Interceptor',
|
||||
'TMDO_LatePoint_Interceptor',
|
||||
'TMDO_Sync_Bridge',
|
||||
|
||||
@@ -424,7 +424,7 @@ class TMDO_Snapshot_Manager {
|
||||
return array();
|
||||
}
|
||||
$out = array();
|
||||
$modules = array_merge( TMDO_Feature_Flags::HPCT_MODULES, TMDO_Feature_Flags::ZONE_MODULES );
|
||||
$modules = array_merge( TMDO_Feature_Flags::ADDON_MODULES, TMDO_Feature_Flags::ZONE_MODULES );
|
||||
foreach ( $modules as $m ) {
|
||||
$out[ $m ] = TMDO_Feature_Flags::get( $m );
|
||||
}
|
||||
|
||||
@@ -65,65 +65,65 @@ class FSMAdvisorTest extends TestCase {
|
||||
}
|
||||
|
||||
public function test_idle_state_returns_HOLD(): void {
|
||||
$this->set_module_state( 'reviews', 'idle' );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'reviews' );
|
||||
$this->set_module_state( 'wc_orders', 'idle' );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'wc_orders' );
|
||||
$this->assertSame( 'HOLD', $advice['action'] );
|
||||
$this->assertSame( 'info', $advice['level'] );
|
||||
}
|
||||
|
||||
public function test_complete_state_returns_HOLD(): void {
|
||||
$this->set_module_state( 'reviews', 'complete' );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'reviews' );
|
||||
$this->set_module_state( 'wc_orders', 'complete' );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'wc_orders' );
|
||||
$this->assertSame( 'HOLD', $advice['action'] );
|
||||
}
|
||||
|
||||
public function test_dual_write_with_short_soak_returns_WAIT(): void {
|
||||
// In dual_write 0 days < 1 day min soak.
|
||||
$this->set_module_state( 'reviews', 'dual_write', 0 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'reviews' );
|
||||
$this->set_module_state( 'wc_orders', 'dual_write', 0 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'wc_orders' );
|
||||
$this->assertSame( 'WAIT', $advice['action'] );
|
||||
$this->assertGreaterThanOrEqual( 1, $advice['days_remaining'] );
|
||||
}
|
||||
|
||||
public function test_dual_write_after_min_soak_returns_PROMOTE(): void {
|
||||
$this->set_module_state( 'reviews', 'dual_write', 2 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'reviews' );
|
||||
$this->set_module_state( 'wc_orders', 'dual_write', 2 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'wc_orders' );
|
||||
$this->assertSame( 'PROMOTE', $advice['action'] );
|
||||
$this->assertSame( 'backfill', $advice['next_state'] );
|
||||
}
|
||||
|
||||
public function test_verify_with_long_soak_returns_PROMOTE(): void {
|
||||
// verify needs 7 days; 10 days = should promote.
|
||||
$this->set_module_state( 'reviews', 'verify', 10 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'reviews' );
|
||||
$this->set_module_state( 'wc_orders', 'verify', 10 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'wc_orders' );
|
||||
$this->assertSame( 'PROMOTE', $advice['action'] );
|
||||
$this->assertSame( 'cutover', $advice['next_state'] );
|
||||
}
|
||||
|
||||
public function test_verify_with_short_soak_returns_WAIT(): void {
|
||||
$this->set_module_state( 'reviews', 'verify', 3 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'reviews' );
|
||||
$this->set_module_state( 'wc_orders', 'verify', 3 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'wc_orders' );
|
||||
$this->assertSame( 'WAIT', $advice['action'] );
|
||||
$this->assertSame( 4, $advice['days_remaining'] );
|
||||
}
|
||||
|
||||
public function test_cleanup_with_short_wash_returns_WAIT(): void {
|
||||
$this->set_module_state( 'reviews', 'cleanup', 1 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'reviews' );
|
||||
$this->set_module_state( 'wc_orders', 'cleanup', 1 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'wc_orders' );
|
||||
$this->assertSame( 'WAIT', $advice['action'] );
|
||||
$this->assertSame( 2, $advice['days_remaining'] );
|
||||
}
|
||||
|
||||
public function test_cleanup_after_wash_returns_PROMOTE(): void {
|
||||
$this->set_module_state( 'reviews', 'cleanup', 5 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'reviews' );
|
||||
$this->set_module_state( 'wc_orders', 'cleanup', 5 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'wc_orders' );
|
||||
$this->assertSame( 'PROMOTE', $advice['action'] );
|
||||
$this->assertSame( 'complete', $advice['next_state'] );
|
||||
}
|
||||
|
||||
public function test_advice_metrics_include_state_and_soak(): void {
|
||||
$this->set_module_state( 'reviews', 'dual_write', 5 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'reviews' );
|
||||
$this->set_module_state( 'wc_orders', 'dual_write', 5 );
|
||||
$advice = WPDO_FSM_Advisor::advise( 'wc_orders' );
|
||||
$this->assertSame( 'dual_write', $advice['metrics']['state'] );
|
||||
$this->assertSame( 5, $advice['metrics']['days_in_state'] );
|
||||
$this->assertSame( 1, $advice['metrics']['min_soak_days'] );
|
||||
@@ -132,8 +132,8 @@ class FSMAdvisorTest extends TestCase {
|
||||
public function test_advise_all_returns_map_for_all_modules(): void {
|
||||
$advice = WPDO_FSM_Advisor::advise_all();
|
||||
$this->assertNotEmpty( $advice );
|
||||
// All known HPCT + zone modules should be present (default idle).
|
||||
foreach ( WPDO_Feature_Flags::HPCT_MODULES as $m ) {
|
||||
// All known AddOn + zone modules should be present (default idle).
|
||||
foreach ( WPDO_Feature_Flags::ADDON_MODULES as $m ) {
|
||||
$this->assertArrayHasKey( $m, $advice );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,15 +67,15 @@ class ModuleDetectorTest extends TestCase {
|
||||
|
||||
public function test_module_rules_known_modules_count(): void {
|
||||
$modules = WPDO_Module_Rules::known_modules();
|
||||
$this->assertGreaterThanOrEqual( 14, count( $modules ), '預期 ≥ 14 個 module(9 HPCT + 6 zone - 1 dup)' );
|
||||
$this->assertContains( 'reviews', $modules );
|
||||
$this->assertGreaterThanOrEqual( 8, count( $modules ), '預期 ≥ 8 個 module(2 AddOn + 6 zone)' );
|
||||
$this->assertContains( 'wc_orders', $modules );
|
||||
$this->assertContains( 'warm', $modules );
|
||||
$this->assertContains( 'archive', $modules );
|
||||
$this->assertContains( 'hot_hp_listing', $modules );
|
||||
}
|
||||
|
||||
public function test_module_rule_for_module_returns_array(): void {
|
||||
$rule = WPDO_Module_Rules::for_module( 'reviews' );
|
||||
$rule = WPDO_Module_Rules::for_module( 'wc_orders' );
|
||||
$this->assertIsArray( $rule );
|
||||
$this->assertArrayHasKey( 'compat_required', $rule );
|
||||
$this->assertArrayHasKey( 'description', $rule );
|
||||
@@ -86,9 +86,9 @@ class ModuleDetectorTest extends TestCase {
|
||||
}
|
||||
|
||||
public function test_detector_skip_when_module_not_idle(): void {
|
||||
// Set reviews to dual_write.
|
||||
WPDO_Feature_Flags::set( 'reviews', 'dual_write' );
|
||||
$r = WPDO_Module_Detector::detect_one( 'reviews' );
|
||||
// Set wc_orders to dual_write.
|
||||
WPDO_Feature_Flags::set( 'wc_orders', 'dual_write' );
|
||||
$r = WPDO_Module_Detector::detect_one( 'wc_orders' );
|
||||
$this->assertSame( 'skip', $r['recommendation'] );
|
||||
$this->assertFalse( $r['available'] );
|
||||
$this->assertNotEmpty( $r['blockers'] );
|
||||
@@ -123,9 +123,9 @@ class ModuleDetectorTest extends TestCase {
|
||||
}
|
||||
|
||||
public function test_detector_hivepress_required_blocks_when_inactive(): void {
|
||||
// reviews requires hivepress; Compatibility class isn't loaded in this test.
|
||||
// hot_hp_listing requires hivepress; Compatibility class isn't loaded in this test.
|
||||
// Without Compatibility class, the helper returns the full required list as missing.
|
||||
$r = WPDO_Module_Detector::detect_one( 'reviews' );
|
||||
$r = WPDO_Module_Detector::detect_one( 'hot_hp_listing' );
|
||||
$this->assertSame( 'skip', $r['recommendation'] );
|
||||
$this->assertNotEmpty( $r['blockers'] );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Core carries no HPCT (HivePress Custom Tables) legacy.
|
||||
*
|
||||
* The HPCT interceptors were removed from the HivePress AddOn (PR-014) and no
|
||||
* site ever ran the HPCT import, so the modules, compatibility probes, admin
|
||||
* tab and CLI command that fed them are gone too. The only survivor is the
|
||||
* `wpdo_hpct_imported` option name in the uninstall cleanup list, so old
|
||||
* installs still get it removed.
|
||||
*/
|
||||
class NoHpctLegacyTest extends TestCase {
|
||||
|
||||
private const ALLOWED = array(
|
||||
'includes/class-tmdo-installer.php' => array( 'wpdo_hpct_imported' ),
|
||||
);
|
||||
|
||||
public function test_core_code_does_not_mention_hpct(): void {
|
||||
$root = dirname( __DIR__, 2 );
|
||||
$offences = array();
|
||||
foreach ( array( 'includes', 'cli', 'admin' ) as $dir ) {
|
||||
$files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $root . '/' . $dir, FilesystemIterator::SKIP_DOTS ) );
|
||||
foreach ( $files as $file ) {
|
||||
if ( 'php' !== $file->getExtension() ) {
|
||||
continue;
|
||||
}
|
||||
$rel = substr( $file->getPathname(), strlen( $root ) + 1 );
|
||||
$code = '';
|
||||
foreach ( token_get_all( (string) file_get_contents( $file->getPathname() ) ) as $token ) {
|
||||
if ( is_array( $token ) && in_array( $token[0], array( T_COMMENT, T_DOC_COMMENT ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
$code .= is_array( $token ) ? $token[1] : $token;
|
||||
}
|
||||
$code = str_replace( self::ALLOWED[ $rel ] ?? array(), '', $code );
|
||||
if ( preg_match_all( '/\w*hpct\w*/i', $code, $m ) ) {
|
||||
$offences[] = $rel . ': ' . implode( ', ', array_unique( $m[0] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertSame( array(), $offences, "HPCT legacy left in core:\n" . implode( "\n", $offences ) );
|
||||
}
|
||||
|
||||
public function test_only_live_addon_modules_remain_besides_zone_modules(): void {
|
||||
$this->assertSame( array( 'wc_orders', 'latepoint' ), TMDO_Feature_Flags::ADDON_MODULES );
|
||||
|
||||
$GLOBALS['_wp_options'] = array();
|
||||
$ref = new ReflectionProperty( TMDO_Feature_Flags::class, 'cache' );
|
||||
$ref->setAccessible( true );
|
||||
$ref->setValue( null, null );
|
||||
|
||||
$modules = array_keys( TMDO_Feature_Flags::all() );
|
||||
foreach ( array( 'reviews', 'messages', 'favorites', 'memberships', 'statistics', 'requests', 'listing_meta' ) as $gone ) {
|
||||
$this->assertNotContains( $gone, $modules );
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user