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
147 lines
4.1 KiB
PHP
147 lines
4.1 KiB
PHP
<?php
|
||
/**
|
||
* Cron optimizer — replaces HivePress' hourly listing-expiry full scan.
|
||
*
|
||
* HivePress core's `class-listing.php::hourly()` walks every `hp_listing`
|
||
* post and reads `hp_expired_time` / `hp_featured_time` postmeta, then
|
||
* dispatches expire actions. With N listings × 2 meta keys this is 2N
|
||
* postmeta lookups per hour.
|
||
*
|
||
* After Sprint 1 added `hp_expired_time` + `hp_featured_time` to
|
||
* `wp_wpdo_hot_hp_listing` as indexed BIGINT columns, we can replace the
|
||
* scan with two indexed range queries: `WHERE expired_time BETWEEN 1 AND
|
||
* UNIX_TIMESTAMP()`.
|
||
*
|
||
* Disabled by default. Operator opts in via:
|
||
*
|
||
* wp option update wpdo_hivepress_cron_optimizer_enabled 1
|
||
*
|
||
* The optimizer hooks into `hivepress/v1/events/hourly` at priority 5
|
||
* (before HP's own callback) and short-circuits the scan when the hot
|
||
* table is reachable. If the hot table is missing or the relevant
|
||
* `hot_hp_listing` module isn't in cutover/complete state, the
|
||
* optimizer yields silently to HP's original code path.
|
||
*
|
||
* @package WP_Data_Optimizer
|
||
* @since 3.0.0
|
||
*/
|
||
|
||
if ( ! defined( 'ABSPATH' ) ) {
|
||
exit;
|
||
}
|
||
|
||
if ( ! class_exists( 'TMDO_HivePress_Cron_Optimizer' ) ) {
|
||
|
||
/**
|
||
* Cron optimizer for HivePress hourly listing expiry.
|
||
*
|
||
* Single-instance, registered via static `register()`.
|
||
*/
|
||
final class TMDO_HivePress_Cron_Optimizer {
|
||
|
||
/** Operator opt-in option key. */
|
||
public const OPTION_ENABLED = 'wpdo_hivepress_cron_optimizer_enabled';
|
||
|
||
/** Module name in TMDO_Feature_Flags terminology. */
|
||
public const MODULE = 'hot_hp_listing';
|
||
|
||
/**
|
||
* Whether `register()` has bound hooks this request.
|
||
*
|
||
* @var bool
|
||
*/
|
||
private static bool $bound = false;
|
||
|
||
/**
|
||
* Bind the hourly hook (idempotent).
|
||
*
|
||
* Called by Bootstrap during core adapter event-hook registration so
|
||
* activation follows the same gate as adapter binding.
|
||
*/
|
||
public static function register(): void {
|
||
if ( self::$bound ) {
|
||
return;
|
||
}
|
||
if ( ! self::is_enabled() ) {
|
||
return;
|
||
}
|
||
add_action( 'hivepress/v1/events/hourly', array( __CLASS__, 'maybe_run' ), 5, 0 );
|
||
self::$bound = true;
|
||
}
|
||
|
||
/**
|
||
* Reset internal state (test only).
|
||
*
|
||
* @internal
|
||
*/
|
||
public static function reset_for_tests(): void {
|
||
self::$bound = false;
|
||
}
|
||
|
||
/**
|
||
* Whether the optimizer is enabled via operator option.
|
||
*/
|
||
public static function is_enabled(): bool {
|
||
if ( ! function_exists( 'get_option' ) ) {
|
||
return false;
|
||
}
|
||
return (bool) (int) get_option( self::OPTION_ENABLED, 0 );
|
||
}
|
||
|
||
/**
|
||
* Hourly hook: dispatch expire actions for listings whose hot-zone
|
||
* `expired_time` falls in (0, NOW()] window.
|
||
*
|
||
* Yields silently when the hot table is missing or the module is not
|
||
* in a state where reads are guaranteed accurate.
|
||
*
|
||
* @return int Number of listings that received the expire action (for tests).
|
||
*/
|
||
public static function maybe_run(): int {
|
||
if ( ! self::module_can_read() ) {
|
||
return 0;
|
||
}
|
||
|
||
global $wpdb;
|
||
if ( ! isset( $wpdb ) || ! is_object( $wpdb ) ) {
|
||
return 0;
|
||
}
|
||
|
||
$table = $wpdb->prefix . 'wpdo_hot_hp_listing';
|
||
$now = time();
|
||
|
||
// Indexed range scan replaces O(N) postmeta walk.
|
||
$ids = (array) $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $table is wpdb prefix + literal.
|
||
$wpdb->prepare(
|
||
"SELECT post_id FROM `{$table}` WHERE expired_time > 0 AND expired_time <= %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $table is wpdb prefix + literal.
|
||
$now
|
||
)
|
||
);
|
||
|
||
$count = 0;
|
||
foreach ( $ids as $post_id ) {
|
||
$post_id = (int) $post_id;
|
||
if ( $post_id <= 0 ) {
|
||
continue;
|
||
}
|
||
do_action( 'hivepress/v1/models/listing/expire', $post_id );
|
||
++$count;
|
||
}
|
||
|
||
return $count;
|
||
}
|
||
|
||
/**
|
||
* Whether the `hot_hp_listing` module is in a state where the hot
|
||
* table reflects current truth (cutover or complete).
|
||
*/
|
||
private static function module_can_read(): bool {
|
||
if ( ! class_exists( 'TMDO_Feature_Flags' ) ) {
|
||
return false;
|
||
}
|
||
return TMDO_Feature_Flags::is_query_active( self::MODULE );
|
||
}
|
||
}
|
||
|
||
} // end if ( ! class_exists )
|