diff --git a/2meet-data-optimizer.php b/2meet-data-optimizer.php index a77c02d..1e41e67 100644 --- a/2meet-data-optimizer.php +++ b/2meet-data-optimizer.php @@ -133,6 +133,7 @@ require_once TMDO_PATH . 'includes/class-tmdo-comment-stress-tester.php'; // ── Interceptor base + Sync Bridge(核心唯一保留 interceptor)───────────── require_once TMDO_PATH . 'includes/interceptors/class-tmdo-interceptor-base.php'; +require_once TMDO_PATH . 'includes/interceptors/class-tmdo-standard-post-interceptor.php'; require_once TMDO_PATH . 'includes/interceptors/class-tmdo-sync-bridge.php'; // ── Zone handlers ───────────────────────────────────────────────────────── diff --git a/includes/class-tmdo-back-compat.php b/includes/class-tmdo-back-compat.php index e32a644..e1513f0 100644 --- a/includes/class-tmdo-back-compat.php +++ b/includes/class-tmdo-back-compat.php @@ -127,6 +127,7 @@ $tmdo_class_aliases = array( 'TMDO_Phase_Completed' => 'WPDO_Phase_Completed', 'TMDO_Zone_Archive' => 'WPDO_Zone_Archive', 'TMDO_Interceptor_Base' => 'WPDO_Interceptor_Base', + 'TMDO_Standard_Post_Interceptor' => 'WPDO_Standard_Post_Interceptor', 'TMDO_Query_Interceptor_Base' => 'WPDO_Query_Interceptor_Base', 'TMDO_Abstract_Notifier' => 'WPDO_Abstract_Notifier', ); diff --git a/includes/interceptors/class-tmdo-standard-post-interceptor.php b/includes/interceptors/class-tmdo-standard-post-interceptor.php new file mode 100644 index 0000000..af21486 --- /dev/null +++ b/includes/interceptors/class-tmdo-standard-post-interceptor.php @@ -0,0 +1,149 @@ + 'column', ... ]; + * + * @package TMDO + * @since 3.0.1 + */ + +// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from TMDO_DB::table(); column validated by subclass FIELD_MAP constant. + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +/** + * Abstract base for standard post→custom-table interceptors. + * + * @since 3.0.1 + */ +abstract class TMDO_Standard_Post_Interceptor extends TMDO_Interceptor_Base { + + // ── Subclass contracts ──────────────────────────────────────────────────── + + /** WordPress post_type slug this interceptor handles. */ + abstract protected function get_post_type(): string; + + /** Key passed to TMDO_DB::table() to resolve the custom table name. */ + abstract protected function get_table_key(): string; + + /** + * Build the INSERT row for a newly created post. + * + * @param int $post_id Post ID. + * @param \WP_Post $post Post object. + * @param string $now Formatted datetime string from TMDO_DB::now(). + * @return array{values: array, formats: array} + * 'values' — associative array of column => value. + * 'formats' — ordered format string array (%d/%s) for $wpdb->insert. + */ + abstract protected function build_insert_data( int $post_id, \WP_Post $post, string $now ): array; + + // ── Shared implementation ───────────────────────────────────────────────── + + /** + * Register the three standard hooks. Subclasses that need additional + * hooks (e.g. get_post_metadata) should call parent::register_hooks() + * first and then add their own. + */ + public function register_hooks(): void { + add_filter( 'update_post_metadata', array( $this, 'filter_update_meta' ), 10, 5 ); + add_action( 'wp_insert_post', array( $this, 'action_insert_post' ), 10, 3 ); + add_action( 'before_delete_post', array( $this, 'action_delete_post' ), 10, 2 ); + } + + /** + * Deletes the custom-table record when the post is deleted. + * + * @param int $post_id Post ID. + * @param \WP_Post $post Post object. + * @return void + */ + final public function action_delete_post( int $post_id, \WP_Post $post ): void { + if ( $this->get_post_type() !== $post->post_type || ! $this->is_active() ) { + return; + } + + try { + global $wpdb; + $wpdb->delete( TMDO_DB::table( $this->get_table_key() ), array( 'post_id' => $post_id ), array( '%d' ) ); + } catch ( \Throwable $e ) { + TMDO_Logger::error( $this->module, 'before_delete_post', $e->getMessage() ); + } + } + + /** + * Syncs a meta update to the custom table column. + * + * @param mixed $check Short-circuit flag. + * @param int $post_id Post ID. + * @param string $meta_key Meta key. + * @param mixed $meta_value Meta value. + * @param mixed $prev_value Previous value (unused). + * @return mixed Unmodified $check. + */ + final public function filter_update_meta( $check, int $post_id, string $meta_key, $meta_value, $prev_value ) { + $field_map = static::FIELD_MAP; // late static binding — picks up subclass const. + if ( $this->get_post_type() !== get_post_type( $post_id ) || ! isset( $field_map[ $meta_key ] ) ) { + return $check; + } + + if ( ! $this->is_active() ) { + return $check; + } + + try { + global $wpdb; + $col = $field_map[ $meta_key ]; + $table = TMDO_DB::table( $this->get_table_key() ); + $wpdb->query( + $wpdb->prepare( + "UPDATE `{$table}` SET `{$col}` = %s, updated_at = %s WHERE post_id = %d", + $meta_value, + TMDO_DB::now(), + $post_id + ) + ); + } catch ( \Throwable $e ) { + TMDO_Logger::error( $this->module, 'update_post_metadata', $e->getMessage() ); + } + + return $check; + } + + /** + * Inserts the custom-table record when a new post is created. + * + * @param int $post_id Post ID. + * @param \WP_Post $post Post object. + * @param bool $update Whether this is an update (not a new post). + * @return void + */ + final public function action_insert_post( int $post_id, \WP_Post $post, bool $update ): void { + if ( $update || $this->get_post_type() !== $post->post_type || ! $this->is_active() ) { + return; + } + + try { + $data = $this->build_insert_data( $post_id, $post, TMDO_DB::now() ); + global $wpdb; + $wpdb->insert( + TMDO_DB::table( $this->get_table_key() ), + $data['values'], + $data['formats'] + ); + } catch ( \Throwable $e ) { + TMDO_Logger::error( $this->module, 'wp_insert_post', $e->getMessage() ); + } + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1c4a5a2..383a423 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -510,6 +510,7 @@ require_once TMDO_PATH . 'includes/class-tmdo-compatibility.php'; // Interceptors. require_once TMDO_PATH . 'includes/interceptors/class-tmdo-interceptor-base.php'; +require_once TMDO_PATH . 'includes/interceptors/class-tmdo-standard-post-interceptor.php'; require_once TMDO_PATH . 'includes/interceptors/class-tmdo-sync-bridge.php'; // Zones. diff --git a/tests/integration/bootstrap.php b/tests/integration/bootstrap.php index 6ed4d1b..a44ddd7 100644 --- a/tests/integration/bootstrap.php +++ b/tests/integration/bootstrap.php @@ -526,6 +526,7 @@ require_once TMDO_PATH . 'includes/class-tmdo-hook-bus-bridge.php'; require_once TMDO_PATH . 'includes/class-tmdo-conflict-monitor.php'; require_once TMDO_PATH . 'includes/class-tmdo-compatibility.php'; require_once TMDO_PATH . 'includes/interceptors/class-tmdo-interceptor-base.php'; +require_once TMDO_PATH . 'includes/interceptors/class-tmdo-standard-post-interceptor.php'; require_once TMDO_PATH . 'includes/interceptors/class-tmdo-sync-bridge.php'; require_once TMDO_PATH . 'includes/zones/class-tmdo-zone-router.php'; require_once TMDO_PATH . 'includes/class-tmdo-routing-predicate.php';