Files
2meet-data-optimizer/includes/engine/class-tmdo-conflict-detector.php
T
wpdev d36bb954d1 chore: initial snapshot of 2meet-data-optimizer v0.1.0
Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 05:06:36 +08:00

149 lines
5.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* TMDO_Conflict_Detector — 偵測 UAE 與 UAEPG 同欄位衝突
*
* 背景:
* UAEPG(PostgreSQL sidecar)以 metadata filter priority 5 攔截,
* UAE 在 priority 10 攔截。若同一 meta_key 被雙方都登錄,
* UAEPG 會先短路 return,UAE 寫入永遠不執行,資料靜默遺失。
*
* 本類別在欄位登錄階段完成後(init:25)掃描雙方 Registry,
* 找出 overlap 並:
* 1. 寫入 error log(供 CI / 監控系統消化)
* 2. 在管理後台顯示紅色 admin_notices
* 3. 提供 TMDO_Conflict_Detector::get_conflicts() 與 CLI 指令 wp uae conflicts 查詢
*
* 結果 cache 在 static property 內,單 request 內不重複掃描。
*
* @package WP_Data_Optimizer
* @since 1.1.2
*/
// phpcs:disable Squiz.Commenting,Generic.Commenting,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber,Generic.CodeAnalysis.UnusedFunctionParameter,Generic.CodeAnalysis.EmptyStatement,Squiz.PHP.DisallowMultipleAssignments,Squiz.PHP.DisallowSizeFunctionsInLoops,WordPress.WP.I18n.MissingTranslatorsComment,WordPress.PHP.NoSilencedErrors,WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents,Squiz.PHP.CommentedOutCode,Universal.NamingConventions.NoReservedKeywordParameterNames,WordPress.PHP.YodaConditions,Squiz.Commenting.InlineComment.InvalidEndChar -- PR-1 ported from UAE; cleanup PR scheduled.
defined( 'ABSPATH' ) || exit;
final class TMDO_Conflict_Detector {
/**
* @var array<int, array{entity_type:string, meta_key:string, wpdo_group:string, uaepg_group:string}>|null
*/
private static ?array $cache = null;
public static function init(): void {
// init:25 — register_default_fields 於 init:20 觸發 wpdo_register_fields,
// UAEPG 亦在 init:20 觸發 uaepg_register_fields;此時 priority 25 可確保雙邊皆完成登錄。
add_action( 'init', array( self::class, 'scan' ), 25 );
if ( is_admin() ) {
add_action( 'admin_notices', array( self::class, 'maybe_render_admin_notice' ) );
}
}
/**
* 掃描並 cache 衝突清單。
*
* @return array<int, array{entity_type:string, meta_key:string, wpdo_group:string, uaepg_group:string}>
*/
public static function scan(): array {
if ( self::$cache !== null ) {
return self::$cache;
}
if ( ! class_exists( 'UAEPG_Registry' ) ) {
return self::$cache = array();
}
$conflicts = array();
foreach ( TMDO_Entity_Registry::get_all_fields() as $entity_type => $fields_by_key ) {
foreach ( $fields_by_key as $meta_key => $wpdo_field ) {
if ( ! UAEPG_Registry::is_managed( $entity_type, $meta_key ) ) {
continue;
}
$uaepg_field = UAEPG_Registry::get_field( $entity_type, $meta_key );
$conflicts[] = array(
'entity_type' => $entity_type,
'meta_key' => $meta_key,
'wpdo_group' => $wpdo_field['group'] ?? '(unknown)',
'uaepg_group' => $uaepg_field['group'] ?? '(unknown)',
);
}
}
if ( $conflicts ) {
TMDO_Logger::error(
'field_registration_conflict',
array(
'count' => count( $conflicts ),
'fields' => $conflicts,
)
);
}
return self::$cache = $conflicts;
}
/**
* 取得衝突清單(若尚未掃描,觸發掃描)。
*/
public static function get_conflicts(): array {
return self::$cache ?? self::scan();
}
/**
* 測試用:強制清除 cache,下次 scan() 會重跑。
*
* @internal
*/
public static function reset_cache(): void {
self::$cache = null;
}
/**
* 若有衝突則輸出管理後台紅色通知。
*/
public static function maybe_render_admin_notice(): void {
$conflicts = self::get_conflicts();
if ( ! $conflicts ) {
return;
}
if ( ! TMDO_Capability::current_user_can_admin() ) {
return;
}
$count = count( $conflicts );
$lines = array();
foreach ( array_slice( $conflicts, 0, 5 ) as $c ) {
$lines[] = sprintf(
'%s / %s(UAE 群組「%s」 ↔ UAEPG 群組「%s」)',
esc_html( $c['entity_type'] ),
esc_html( $c['meta_key'] ),
esc_html( $c['wpdo_group'] ),
esc_html( $c['uaepg_group'] )
);
}
$extra = $count > 5 ? sprintf( __( '… 另有 %d 個欄位未顯示', 'uae' ), $count - 5 ) : '';
printf(
'<div class="notice notice-error"><p><strong>%s</strong></p><ul style="list-style:disc;margin-left:20px;"><li>%s</li></ul>%s<p>%s <code>wp uae conflicts</code></p></div>',
esc_html(
sprintf(
/* translators: %d: conflict count */
__( 'UAE × UAEPG 欄位衝突偵測:發現 %d 個同 meta_key 被雙方 Registry 重複登錄 — UAEPG 會搶先短路,UAE 寫入將被靜默跳過', 'uae' ),
$count
)
),
// $lines elements were each individually esc_html()'d above (lines 121-124).
// implode joins escaped strings with literal HTML markers — safe.
implode( '</li><li>', $lines ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- pre-escaped above.
$extra ? '<p><em>' . esc_html( $extra ) . '</em></p>' : '',
esc_html__( '查看完整清單:', 'uae' )
);
}
}