b4400a68e5
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
167 lines
5.0 KiB
PHP
167 lines
5.0 KiB
PHP
<?php
|
|
/**
|
|
* Comment query router for HivePress comment-type models.
|
|
*
|
|
* Rewrites `WP_Comment_Query` SQL clauses to JOIN the per-addon shadow tables
|
|
* built by Sprint 2 adapters. Currently handles four `comment_type` values:
|
|
*
|
|
* hp_message → wp_wpdo_comment_hp_message (recipient_id, is_read indexed)
|
|
* hp_favorite → wp_wpdo_comment_hp_favorite (UNIQUE user_id+listing_id)
|
|
* hp_offer → wp_wpdo_comment_hp_offer (request_id, approved indexed)
|
|
* hp_review → wp_wpdo_comment_hp_review (managed by entity-bridge)
|
|
*
|
|
* Disabled by default. Operator opts in via:
|
|
*
|
|
* wp option update wpdo_hivepress_comment_router_enabled 1
|
|
*
|
|
* Once enabled, comment queries with `type` matching one of the above gain a
|
|
* shadow-table LEFT JOIN at `comments_clauses` filter time. The JOIN is
|
|
* deliberately LEFT so missing shadow rows (during dual_write phase before
|
|
* backfill completes) don't drop legitimate comments.
|
|
*
|
|
* Per Karpathy guideline: this router only handles the four comment_types
|
|
* Sprint 2 actually needs — no extension hooks for "future addons".
|
|
*
|
|
* @package WP_Data_Optimizer
|
|
* @since 3.0.0
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
if ( ! class_exists( 'TMDO_HivePress_Comment_Router' ) ) {
|
|
|
|
/**
|
|
* Comment query rewriter.
|
|
*
|
|
* Single-instance, registered via static `register()`. Stateless apart from
|
|
* the option-cached enabled flag.
|
|
*/
|
|
final class TMDO_HivePress_Comment_Router {
|
|
|
|
/** Option key controlling the rewriter. */
|
|
public const OPTION_ENABLED = 'wpdo_hivepress_comment_router_enabled';
|
|
|
|
/**
|
|
* Map of comment_type → shadow table (without wpdb prefix).
|
|
*
|
|
* @var array<string,string>
|
|
*/
|
|
private const SHADOW_MAP = array(
|
|
'hp_message' => 'wpdo_comment_hp_message',
|
|
'hp_favorite' => 'wpdo_comment_hp_favorite',
|
|
'hp_offer' => 'wpdo_comment_hp_offer',
|
|
'hp_review' => 'wpdo_comment_hp_review',
|
|
);
|
|
|
|
/**
|
|
* Whether the router is bound this request.
|
|
*
|
|
* @var bool
|
|
*/
|
|
private static bool $bound = false;
|
|
|
|
/**
|
|
* Bind the comments_clauses filter (idempotent).
|
|
*
|
|
* Bootstrap calls this from each adapter's on_register_query_hooks()
|
|
* so router activation follows the same gate as adapter binding.
|
|
*/
|
|
public static function register(): void {
|
|
if ( self::$bound ) {
|
|
return;
|
|
}
|
|
if ( ! self::is_enabled() ) {
|
|
return;
|
|
}
|
|
add_filter( 'comments_clauses', array( __CLASS__, 'rewrite' ), 20, 2 );
|
|
self::$bound = true;
|
|
}
|
|
|
|
/**
|
|
* Reset internal state (test only).
|
|
*
|
|
* @internal
|
|
*/
|
|
public static function reset_for_tests(): void {
|
|
self::$bound = false;
|
|
}
|
|
|
|
/**
|
|
* Whether router is enabled via the operator option.
|
|
*/
|
|
public static function is_enabled(): bool {
|
|
if ( ! function_exists( 'get_option' ) ) {
|
|
return false;
|
|
}
|
|
return (bool) (int) get_option( self::OPTION_ENABLED, 0 );
|
|
}
|
|
|
|
/**
|
|
* Filter callback: rewrite comments_clauses for known hp comment types.
|
|
*
|
|
* @param array $clauses SQL clauses (join, where, fields, ...).
|
|
* @param \WP_Comment_Query $query The query object.
|
|
* @return array Possibly-modified clauses.
|
|
*/
|
|
public static function rewrite( array $clauses, $query ): array {
|
|
$type = self::query_comment_type( $query );
|
|
if ( '' === $type || ! isset( self::SHADOW_MAP[ $type ] ) ) {
|
|
return $clauses;
|
|
}
|
|
|
|
global $wpdb;
|
|
$shadow = $wpdb->prefix . self::SHADOW_MAP[ $type ];
|
|
$alias = 'wpdo_shadow';
|
|
|
|
// LEFT JOIN keeps row visibility when shadow lags (during backfill).
|
|
$join = ( $clauses['join'] ?? '' ) . " LEFT JOIN `{$shadow}` AS {$alias} ON {$alias}.comment_id = {$wpdb->comments}.comment_ID";
|
|
|
|
// Filter pushdown: hp_message recipient lookup uses comment_karma in HP.
|
|
// We rewrite WHERE clauses that filter by comment_karma (= recipient hack)
|
|
// to use the indexed shadow column.
|
|
$where = (string) ( $clauses['where'] ?? '' );
|
|
if ( 'hp_message' === $type && '' !== $where ) {
|
|
$where = preg_replace(
|
|
'/' . preg_quote( $wpdb->comments, '/' ) . '\.comment_karma\s*=\s*(\d+)/',
|
|
$alias . '.recipient_id = $1',
|
|
$where
|
|
);
|
|
}
|
|
|
|
$clauses['join'] = $join;
|
|
$clauses['where'] = $where;
|
|
|
|
/**
|
|
* Filter: wpdo_hivepress_comment_router_clauses
|
|
*
|
|
* Allows fine-grained tweaks per comment_type. Intended for adapter
|
|
* tests + future custom-column lookups.
|
|
*
|
|
* @param array $clauses Modified clauses.
|
|
* @param string $type Matched comment_type.
|
|
* @param string $alias Shadow table alias.
|
|
*/
|
|
return apply_filters( 'wpdo_hivepress_comment_router_clauses', $clauses, $type, $alias );
|
|
}
|
|
|
|
/**
|
|
* Extract the comment_type from a WP_Comment_Query.
|
|
*
|
|
* @param \WP_Comment_Query $query Query object.
|
|
*/
|
|
private static function query_comment_type( $query ): string {
|
|
if ( ! is_object( $query ) || ! isset( $query->query_vars ) ) {
|
|
return '';
|
|
}
|
|
$type = $query->query_vars['type'] ?? '';
|
|
if ( is_array( $type ) ) {
|
|
$type = reset( $type );
|
|
}
|
|
return is_string( $type ) ? $type : '';
|
|
}
|
|
}
|
|
|
|
} // end if ( ! class_exists )
|