d52d604d7a
Anti-EAV Lint + Quality Gate / anti-eav-lint (push) Successful in 8s
Tests / Unit Tests (push) Successful in 10s
Tests / Integration Tests (push) Successful in 35s
Tests / PHP Lint (push) Successful in 7s
Tests / PHPCS (push) Successful in 19s
Tests / PHPStan (push) Successful in 24s
admin/ 的 inline style= 由 348 處降到 9 處,剩下的 9 處全是 CSS custom property 載體(--wpdo-bar-width / --wpdo-cov-pct 等動態數值),與來源外掛的 設計一致。 - wpdo-admin.css 543 → 911 行:補上 148 個 class(來源檔在 selector 與 rule-block 層級都是既有內容的超集,逐條核對過),另加 .wpdo-form-inline - 5 個變數改為 class 版本:$mode_cls / $badge_mode_cls / $diffs_cls / $ap_cls / $e_mode_cls;3 個 stress-test template 補 $mode_text_cls / $mode_card_cls - 刪掉因此變成孤兒的 $mode_badge / $style / $mode_style / $badge_style / $e_mode_bg / $badge_bg - entity card 改 .wpdo-eb-card、pipeline dot 改 .wpdo-stage-pill、demote 按鈕 改 .wpdo-eb-btn-dim;.wpdo-native-counts / .wpdo-recommendation / .wpdo-setup-banner-btn 的重複 inline style 移除 - dashboard 的 top-views 查詢一併改用上一個 commit 的 TMDO_Zone_Warm::VIEW_KEY PHPStan 抓出自動轉換造成的 11 個未定義變數,已全數補回定義並複驗。
224 lines
11 KiB
PHP
224 lines
11 KiB
PHP
<?php
|
||
/**
|
||
* TMDO_Help_Tabs — Contextual help tabs (v2.3.0 M8).
|
||
*
|
||
* Registers `add_help_tab()` content on the WPDO admin page. Each tab gets a
|
||
* dedicated help panel that explains:
|
||
* - what this tab is for
|
||
* - what to do here as a first-time user
|
||
* - links to Entity Bridge, Snapshots, Doctor when relevant
|
||
*
|
||
* Hooked on `load-tools_page_wp-data-optimizer` so help tabs only appear on
|
||
* our admin page (not site-wide).
|
||
*
|
||
* @package WP_Data_Optimizer
|
||
*/
|
||
|
||
declare(strict_types=1);
|
||
|
||
if ( ! defined( 'ABSPATH' ) ) {
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* Help tab registrar — stateless static API.
|
||
*/
|
||
class TMDO_Help_Tabs {
|
||
|
||
/**
|
||
* Hook into admin page load.
|
||
*
|
||
* @return void
|
||
*/
|
||
public static function register(): void {
|
||
add_action( 'load-tools_page_wp-data-optimizer', array( __CLASS__, 'add_tabs' ) );
|
||
}
|
||
|
||
/**
|
||
* Register help tabs based on current `tab` GET param.
|
||
*
|
||
* @return void
|
||
*/
|
||
public static function add_tabs(): void {
|
||
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
|
||
if ( null === $screen ) {
|
||
return;
|
||
}
|
||
$tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( (string) $_GET['tab'] ) ) : 'dashboard'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||
|
||
// Always-on overview tab.
|
||
$screen->add_help_tab(
|
||
array(
|
||
'id' => 'wpdo-help-overview',
|
||
'title' => __( '什麼是 WPDO', '2meet-data-optimizer' ),
|
||
'content' => self::content_overview(),
|
||
)
|
||
);
|
||
|
||
// Per-tab help (matches whichever tab is active).
|
||
$tab_help = array(
|
||
'dashboard' => array( __( '儀表板閱讀指南', '2meet-data-optimizer' ), 'content_dashboard' ),
|
||
'zones' => array( __( '4 個 Zone 是什麼', '2meet-data-optimizer' ), 'content_zones' ),
|
||
'classifier' => array( __( 'Classifier 解讀', '2meet-data-optimizer' ), 'content_classifier' ),
|
||
'snapshots' => array( __( '備份策略', '2meet-data-optimizer' ), 'content_snapshots' ),
|
||
'conflicts' => array( __( '衝突處理', '2meet-data-optimizer' ), 'content_conflicts' ),
|
||
'doctor' => array( __( '健康檢查解讀', '2meet-data-optimizer' ), 'content_doctor' ),
|
||
'logs' => array( __( '日誌使用', '2meet-data-optimizer' ), 'content_logs' ),
|
||
'rest-api' => array( __( 'REST API', '2meet-data-optimizer' ), 'content_rest_api' ),
|
||
);
|
||
if ( isset( $tab_help[ $tab ] ) ) {
|
||
[ $title, $cb ] = $tab_help[ $tab ];
|
||
$screen->add_help_tab(
|
||
array(
|
||
'id' => 'wpdo-help-' . $tab,
|
||
'title' => $title,
|
||
'content' => call_user_func( array( __CLASS__, $cb ) ),
|
||
)
|
||
);
|
||
}
|
||
|
||
// Sidebar with persistent links.
|
||
$screen->set_help_sidebar(
|
||
'<p><strong>' . esc_html__( '更多資源', '2meet-data-optimizer' ) . '</strong></p>'
|
||
. '<p><a href="' . esc_url( admin_url( 'tools.php?page=wp-data-optimizer&tab=entity-bridge' ) ) . '">' . esc_html__( 'Entity Bridge — 主維運入口', '2meet-data-optimizer' ) . '</a></p>'
|
||
. '<p><a href="' . esc_url( admin_url( 'site-health.php' ) ) . '">' . esc_html__( 'WP Site Health', '2meet-data-optimizer' ) . '</a></p>'
|
||
. '<p><code>wp wpdo doctor</code><br/><code>wp wpdo mode-audit</code><br/><code>wp wpdo snapshot create</code></p>'
|
||
);
|
||
}
|
||
|
||
// ─── content templates ─────────────────────────────────────────────
|
||
|
||
/**
|
||
* Overview help content.
|
||
*
|
||
* @return string
|
||
*/
|
||
private static function content_overview(): string {
|
||
return '<p>' . esc_html__( 'WP Data Optimizer 是反 EAV(meta 爆炸)的解方。', '2meet-data-optimizer' ) . '</p>'
|
||
. '<p>' . esc_html__( '核心概念:把 wp_postmeta 的高頻欄位(Hot)、TTL 暫存(Warm)、低頻欄位(Cold)、歷史資料(Archive)拆到 4 種專用表,讀寫快很多、autoload 不再爆。', '2meet-data-optimizer' ) . '</p>'
|
||
. '<p><strong>' . esc_html__( '建議第一步:', '2meet-data-optimizer' ) . '</strong> ' . esc_html__( '逛一遍儀表板了解現況 → 看 Entity Bridge tab 各 entity 健康卡片 → 從 1 個 entity 開始用 Migration Wizard 漸進升級 mode。', '2meet-data-optimizer' ) . '</p>';
|
||
}
|
||
|
||
/**
|
||
* Dashboard help content.
|
||
*
|
||
* @return string
|
||
*/
|
||
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__( '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>'
|
||
. '<li>' . esc_html__( 'Archive 統計 — 壓縮率、依 post_type 拆分', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li>' . esc_html__( 'REST API rate limit — 429 事件 + Top 10 受限 post', '2meet-data-optimizer' ) . '</li>'
|
||
. '</ul>';
|
||
}
|
||
|
||
/**
|
||
* Zones help content.
|
||
*
|
||
* @return string
|
||
*/
|
||
private static function content_zones(): string {
|
||
return '<p>' . esc_html__( '4 個 Zone 對應不同存取頻率與保留需求:', '2meet-data-optimizer' ) . '</p>'
|
||
. '<ul class="wpdo-list-disc">'
|
||
. '<li><strong>Hot</strong> — ' . esc_html__( '高頻索引欄位,如 listing 的 price / location。獨立 column + index,WP_Query 可 JOIN。', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>Warm</strong> — ' . esc_html__( 'TTL 暫存(如 view count、cache stats)。固定表 wp_wpdo_warm 含 expires_at。', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>Cold</strong> — ' . esc_html__( '低頻 meta(settings / preferences)。讀寫透過 interceptor 攔截後保持 EAV 形式。', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>Archive</strong> — ' . esc_html__( 'Trashed / 90+ 天舊資料。可 gzip 壓縮。', '2meet-data-optimizer' ) . '</li>'
|
||
. '</ul>'
|
||
. '<p>' . esc_html__( '不確定要哪種 → 用 Classifier,它會看 access pattern 給建議。', '2meet-data-optimizer' ) . '</p>';
|
||
}
|
||
|
||
/**
|
||
* Classifier help content.
|
||
*
|
||
* @return string
|
||
*/
|
||
private static function content_classifier(): string {
|
||
return '<p>' . esc_html__( 'Classifier 分析 wp_postmeta 給每個 meta_key 一個 zone 建議:', '2meet-data-optimizer' ) . '</p>'
|
||
. '<ul class="wpdo-list-disc">'
|
||
. '<li><strong>Confidence</strong> — ' . esc_html__( '0.0~1.0,越高代表分類越確定。≥ 0.8 可放心採納,< 0.5 建議 manual review。', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>Reasons</strong> — ' . esc_html__( '說明為什麼建議這個 zone(access frequency / row count / TTL hints)。', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>Already-assigned</strong> — ' . esc_html__( '已透過 Schema_Registry 註冊的 meta_key 數量。', '2meet-data-optimizer' ) . '</li>'
|
||
. '</ul>';
|
||
}
|
||
|
||
/**
|
||
* Snapshots help content.
|
||
*
|
||
* @return string
|
||
*/
|
||
private static function content_snapshots(): string {
|
||
return '<p>' . esc_html__( '快照保留政策(v2.2.0):', '2meet-data-optimizer' ) . '</p>'
|
||
. '<ul class="wpdo-list-disc">'
|
||
. '<li>' . esc_html__( '預設 30 天 TTL,可在 wp wpdo snapshot create 時用 --retention-days 覆蓋。', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li>' . esc_html__( 'pre_uninstall / pre_v2_upgrade triggers 受 size-cap 保護(不會被自動 evict)。', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li>' . esc_html__( '檔案存於 wp-content/uploads/wpdo-backups/,含 .htaccess deny all + 每個檔 sha256 校驗。', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li>' . esc_html__( '小於 5MB 自動 inline 到 wp_wpdo_snapshots.inline_blob,方便 wp db export 時跟著走。', '2meet-data-optimizer' ) . '</li>'
|
||
. '</ul>'
|
||
. '<p><strong>' . esc_html__( '災難還原 drill', '2meet-data-optimizer' ) . '</strong>:'
|
||
. esc_html__( '建議每月做一次 dry-run 還原驗證 — wp wpdo snapshot restore <id>(不加 --apply)即可預覽會還原什麼。', '2meet-data-optimizer' ) . '</p>';
|
||
}
|
||
|
||
/**
|
||
* Conflicts help content.
|
||
*
|
||
* @return string
|
||
*/
|
||
private static function content_conflicts(): string {
|
||
return '<p>' . esc_html__( 'Hook 衝突偵測:當多個 plugin 在同一 WordPress 的 metadata filter 上掛 callback 時,可能造成資料寫入順序不確定 / 重複處理。', '2meet-data-optimizer' ) . '</p>'
|
||
. '<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>';
|
||
}
|
||
|
||
/**
|
||
* Doctor help content.
|
||
*
|
||
* @return string
|
||
*/
|
||
private static function content_doctor(): string {
|
||
return '<p>' . esc_html__( '7 項自動健康檢查:', '2meet-data-optimizer' ) . '</p>'
|
||
. '<ol class="wpdo-list-disc">'
|
||
. '<li><strong>schema_drift</strong> — ' . esc_html__( '所有 v2 表是否存在', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>error_budget</strong> — ' . esc_html__( '7 天內 wp_wpdo_errors 行數', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>hook_conflicts</strong> — ' . esc_html__( '同上 conflicts tab', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>autoload_bloat</strong> — ' . esc_html__( 'wp_options autoload 大小 > 5MB 警告', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>postmeta_explosion</strong> — ' . esc_html__( 'wp_postmeta > 5M 行', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>orphan_zone_rows</strong> — ' . esc_html__( '已 idle 的 module 但 zone 表還有資料', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li><strong>missing_snapshot</strong> — ' . esc_html__( '在 cutover/cleanup/complete 但 7 天沒 snapshot', '2meet-data-optimizer' ) . '</li>'
|
||
. '</ol>'
|
||
. '<p>' . esc_html__( '結果有 5 分鐘 transient cache,剛操作完想立刻看新值請等下個週期。', '2meet-data-optimizer' ) . '</p>';
|
||
}
|
||
|
||
/**
|
||
* Logs help content.
|
||
*
|
||
* @return string
|
||
*/
|
||
private static function content_logs(): string {
|
||
return '<p>' . esc_html__( '日誌讀取:', '2meet-data-optimizer' ) . '</p>'
|
||
. '<ul class="wpdo-list-disc">'
|
||
. '<li>' . esc_html__( '每筆對應 wp_wpdo_errors 一行:module / zone / hook / message / timestamp。', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li>' . esc_html__( '預設保留 90 天(wpdo_errors_gc daily cron 自動清)。', '2meet-data-optimizer' ) . '</li>'
|
||
. '<li>' . esc_html__( '看 message 開頭 [WARN] 是 warning level(不影響運作但需注意)。', '2meet-data-optimizer' ) . '</li>'
|
||
. '</ul>';
|
||
}
|
||
|
||
/**
|
||
* REST API help content.
|
||
*
|
||
* @return string
|
||
*/
|
||
private static function content_rest_api(): string {
|
||
return '<p>' . esc_html__( 'REST API 提供 zone 操作 + diagnostics endpoints。', '2meet-data-optimizer' ) . '</p>'
|
||
. '<p>' . esc_html__( '所有 endpoint 用 X-WP-Nonce 認證;rate limit 預設 30/min。', '2meet-data-optimizer' ) . '</p>';
|
||
}
|
||
}
|