<?php
/**
 * Plugin Name: AIEO Action Scheduler Core
 * Description: Consolidated Action Scheduler performance optimizations and emergency controls. Bundled by ai-eshop-optimizer; installed into wp-content/mu-plugins/ on plugin activation.
 * Version: 2.0.0
 * Author: Oxford Metadata
 *
 * This file consolidates ALL Action Scheduler interventions:
 * - Query limiting (memory protection)
 * - Emergency blocker (stop runaway scheduling)
 * - Admin page protection
 * - AJAX protection
 * - Retention period management
 * - Master bypass switch for testing
 *
 * NAMESPACE: As of v2.0.0 (2026-05-12), functions/constants/options are
 * renamed from the legacy `om_as_*` / `OM_AS_*` namespace to `aieo_as_*` /
 * `AIEO_AS_*`. Back-compat aliases are defined at the bottom of this file
 * so any external code (e.g. om-runtime mu-plugins) that still checks for
 * `AIEO_AS_BYPASS_ALL` / `AIEO_AS_BLOCKED_HOOKS` / etc. keeps working.
 *
 * LOAD ORDER: This file MUST load before other plugins (0- prefix ensures this)
 */

if (!defined('ABSPATH')) {
    exit;
}


// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Reason: Read-only $_GET reads used to render admin notices/views; values are sanitized at read time.
// Prevent multiple loads (both new and legacy guard for safety).
if (defined('AIEO_AS_CORE_LOADED') || defined('OM_AS_CORE_LOADED')) {
    return;
}
define('AIEO_AS_CORE_LOADED', true);

// ============================================================================
// CONFIGURATION - Read options ONCE at load time using direct SQL
// ============================================================================
// CRITICAL: We cannot call get_option() from within query filters!
// That causes infinite recursion. Read options via direct SQL BEFORE
// registering any query filters.

global $wpdb, $aieo_as_config;
$aieo_as_config = [
    'emergency_blocker_enabled' => false,
    'bypass_all' => false,
    'debug' => false,
];

if (isset($wpdb) && $wpdb->options) {
    // Each option: read the new aieo_ key first; fall back to the legacy om_
    // key when the new key is missing. This is forward-compatible (operator
    // toggles via the AS Cleanup admin page write to the new key) and lets
    // any pre-rename stored setting persist without a separate migration.

    // Emergency blocker toggle
    $row = $wpdb->get_row($wpdb->prepare(
        "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
        'aieo_as_emergency_blocker_enabled'
    ));
    if (!$row) {
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
            'om_as_emergency_blocker_enabled'
        ));
    }
    if ($row) {
        $aieo_as_config['emergency_blocker_enabled'] = (bool) $row->option_value;
    }

    // Master bypass switch (disables ALL our AS interventions)
    $row = $wpdb->get_row($wpdb->prepare(
        "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
        'aieo_as_bypass_all_optimizations'
    ));
    if (!$row) {
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
            'om_as_bypass_all_optimizations'
        ));
    }
    if ($row) {
        $aieo_as_config['bypass_all'] = (bool) $row->option_value;
    }
}

// Define constants for other files to check
define('AIEO_AS_BYPASS_ALL', $aieo_as_config['bypass_all']);
define('AIEO_AS_DEBUG', $aieo_as_config['debug']);

// ============================================================================
// HELPER FUNCTIONS
// ============================================================================

function aieo_as_is_bypass_enabled() {
    return AIEO_AS_BYPASS_ALL;
}

function aieo_as_is_emergency_blocker_enabled() {
    global $aieo_as_config;
    if (AIEO_AS_BYPASS_ALL) {
        return false;
    }
    return (bool) $aieo_as_config['emergency_blocker_enabled'];
}

/**
 * Detect request type for conditional logic
 */
function aieo_as_get_request_type() {
    static $type = null;
    if ($type !== null) {
        return $type;
    }

    $uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ?? '' ) );

    if (defined('WP_CLI') && WP_CLI) {
        $type = 'cli';
    } elseif (defined('DOING_CRON') && DOING_CRON) {
        $type = 'cron';
    } elseif (strpos($uri, '/wp-cron.php') !== false) {
        $type = 'cron';
    } elseif (isset($_GET['wc-ajax'])) {
        $type = 'wc_ajax';
    } elseif (strpos($uri, 'admin-ajax.php') !== false) {
        $type = 'admin_ajax';
    } elseif (strpos($uri, '/wp-json/') !== false) {
        $type = 'rest_api';
    } elseif (strpos($uri, '/wp-admin/') !== false) {
        $type = 'admin';
    } else {
        $type = 'frontend';
    }

    return $type;
}

/**
 * Check if this request needs Action Scheduler to run
 */
function aieo_as_request_needs_as() {
    $type = aieo_as_get_request_type();

    // CLI and Cron ALWAYS need AS
    if (in_array($type, ['cli', 'cron'], true)) {
        return true;
    }

    $uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ?? '' ) );
    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only inspection of $_REQUEST['action'] to decide whether to short-circuit Action Scheduler boot; no state mutation.
    $action = sanitize_text_field( wp_unslash( $_REQUEST['action'] ?? '' ) );

    // WC Admin pages need AS for order imports
    if (strpos($uri, 'page=wc-admin') !== false) {
        return true;
    }

    // Action Scheduler admin page
    if (strpos($uri, 'page=action-scheduler') !== false) {
        return true;
    }

    // AS Cleanup page — both legacy slug (tools.php?page=om-as-cleanup) and
    // the AIEO DMM subtab (admin.php?page=ai-eshop-dmm&dmm_tab=as_cleanup).
    if (strpos($uri, 'page=om-as-cleanup') !== false) {
        return true;
    }
    if (strpos($uri, 'dmm_tab=as_cleanup') !== false) {
        return true;
    }

    // Specific AJAX actions that need AS
    $as_needed_actions = [
        'action_scheduler_run_scheduled_actions',
        'action_scheduler_run_queue',
        'as_async_request_queue_runner',
        'wc-admin',
        'woocommerce_run_update',
        'woocommerce_run_db_update',
        'om_profiler_get_analysis',
        'om_as_cleanup_step',         // legacy AS Dashboard cleanup AJAX (retired)
        'aieo_dmm_as_cleanup_step',   // AIEO DMM AS Cleanup module AJAX
    ];

    if (in_array($action, $as_needed_actions, true)) {
        return true;
    }

    return false;
}

// ============================================================================
// BLOCKED HOOKS LIST (for emergency blocker)
// ============================================================================

define('AIEO_AS_BLOCKED_HOOKS', [
    // AutomateWoo
    'automatewoo/jobs/clean_inactive_carts',
    'automatewoo_midnight',
    'automatewoo/jobs/abandoned_carts/start',
    'automatewoo/jobs/run_queued_workflows/start',
    'automatewoo/jobs/check_for_gmt_offset_change',
    'automatewoo/jobs/products_goes_on_sale',
    'automatewoo_scheduled_notifications',
    'automatewoo/jobs/check_midnight_job',
    'automatewoo/jobs/delete_failed_queued_workflows/start',
    'automatewoo/jobs/setup_guest_customers/start',
    'automatewoo/jobs/delete_expired_coupons/start',
    'automatewoo/jobs/batched_workflows/process_item',
    'automatewoo/jobs/batched_workflows/create_batch',
    // AIOSEO
    'aioseo_search_statistics_get_latest_available_date',
    'aioseo_search_statistics_objects_scan',
    'aioseo_search_statistics_sitemap_sync',
    'aioseo_search_statistics_site_check',
    'aioseo_ai_get_access_token',
    'aioseo_cache_prune',
    'aioseo_redirects_clear_logs',
    'aioseo_ai_update_credits',
    'aioseo_redirects_clear_404_logs',
    'aioseo_seo_analysis_terms_scan',
    'aioseo_seo_analysis_posts_scan',
    'aioseo_report_summary',
    'aioseo_image_sitemap_scan',
    // WooCommerce
    'woocommerce_cleanup_draft_orders',
    // Rocket Insights
    'rocket_insights_auto_add_homepage',
]);

define('AIEO_AS_BLOCKED_PATTERNS', [
    'automatewoo/',
    'automatewoo_',
    'aioseo_',
    'rocket_insights_',
]);

// ============================================================================
// LEGACY OM_AS_* ALIASES — back-compat for external code
// ============================================================================
// `om-runtime/om-runtime-misc/fix-aioseo-frontend-performance.php` and any
// other code that still checks `defined('OM_AS_BYPASS_ALL')` /
// `OM_AS_BLOCKED_HOOKS` / `OM_AS_BLOCKED_PATTERNS` continues to work.
// Function aliases are declared further down (need definitions first).
if (!defined('OM_AS_CORE_LOADED'))     { define('OM_AS_CORE_LOADED',     AIEO_AS_CORE_LOADED); }
if (!defined('OM_AS_BYPASS_ALL'))      { define('OM_AS_BYPASS_ALL',      AIEO_AS_BYPASS_ALL); }
if (!defined('OM_AS_DEBUG'))           { define('OM_AS_DEBUG',           AIEO_AS_DEBUG); }
if (!defined('OM_AS_BLOCKED_HOOKS'))   { define('OM_AS_BLOCKED_HOOKS',   AIEO_AS_BLOCKED_HOOKS); }
if (!defined('OM_AS_BLOCKED_PATTERNS')){ define('OM_AS_BLOCKED_PATTERNS',AIEO_AS_BLOCKED_PATTERNS); }

// ============================================================================
// SKIP MOST INTERVENTIONS IF BYPASS IS ENABLED
// BUT KEEP MEMORY SAFETY (query limits) TO PREVENT CRASHES
// ============================================================================

if (AIEO_AS_BYPASS_ALL) {
    // Set retention period
    if (!defined('ACTION_SCHEDULER_RETENTION_PERIOD')) {
        define('ACTION_SCHEDULER_RETENTION_PERIOD', 3 * HOUR_IN_SECONDS);
    }
    add_filter('action_scheduler_retention_period', function() {
        return ACTION_SCHEDULER_RETENTION_PERIOD;
    });

    // CRITICAL: Block AS queries on admin pages to prevent AIOSEO isScheduled() crashes
    // AIOSEO runs getScheduledActions() during init which loads ALL pending actions
    $uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ?? '' ) );
    $is_admin = strpos($uri, '/wp-admin/') !== false;
    $is_as_page = strpos($uri, 'page=action-scheduler') !== false;
    $is_cron = strpos($uri, 'wp-cron.php') !== false || (defined('DOING_CRON') && DOING_CRON);
    $is_cli = defined('WP_CLI') && WP_CLI;

    // Block heavy AS queries on admin pages (except AS admin page, cron, CLI,
    // and contexts aieo_as_request_needs_as() flags — AIEO DMM AS Cleanup AJAX,
    // the AS Cleanup subtab page render, WC admin, etc.).
    if ($is_admin && !$is_as_page && !$is_cron && !$is_cli && !aieo_as_request_needs_as()) {
        add_filter('query', function($query) {
            // Only modify AS queries
            if (strpos($query, 'actionscheduler_actions') === false &&
                strpos($query, 'actionscheduler_groups') === false) {
                return $query;
            }

            $normalized = trim(preg_replace('/\s+/', ' ', $query));

            // Not a SELECT - allow INSERT/UPDATE/DELETE
            if (stripos(trim($normalized), 'SELECT') !== 0) {
                return $query;
            }

            // Allow COUNT queries (dashboard stats)
            if (preg_match('/SELECT\s+(COUNT|count)\s*\(/i', $normalized)) {
                return $query;
            }

            // Allow queries with small LIMIT (<=100)
            if (preg_match('/LIMIT\s+(\d+)/i', $normalized, $m) && (int)$m[1] <= 100) {
                return $query;
            }

            // Block unlimited queries - return empty result
            global $wpdb;
            return "SELECT 0 as action_id FROM {$wpdb->posts} WHERE 1=0";
        }, -999999);
    }

    // Block AS queue runner on non-processing request types to prevent crashes.
    // CRITICAL: Cron, CLI, and the AS async runner MUST be allowed through —
    // they are the contexts where Action Scheduler processes pending actions.
    // Blocking them globally causes infinite table growth (actions scheduled
    // but never processed/cleaned, leading to 100,000s of orphaned rows).
    // 2026-05-27: Re-enabled async runner dispatch so AS can process actions
    // BETWEEN 5-minute cron intervals. Without this, peak-traffic sites
    // accumulate 100+ overdue actions per cron gap. The async runner fires a
    // dedicated admin-ajax request that runs in the background — it doesn't
    // block page rendering. aieo-as-ajax-query-protection.php already
    // whitelists as_async_request_queue_runner.
    $is_as_runner = isset($_REQUEST['action']) && $_REQUEST['action'] === 'as_async_request_queue_runner';
    if (!$is_cron && !$is_cli && !$is_as_runner) {
        add_filter('action_scheduler_queue_runner_concurrent_batches', '__return_zero', 999);
        add_filter('action_scheduler_run_schedule', '__return_false', 999);
    }

    // Limit query args
    add_filter('action_scheduler_query_actions_query_args', function($args) {
        $args['per_page'] = isset($args['per_page']) ? min($args['per_page'], 25) : 25;
        return $args;
    }, 999);

    // Skip hook blocking interventions (allow scheduling)
    return;
}

// ============================================================================
// LAYER 1: RETENTION PERIOD (always active unless bypassed)
// ============================================================================

if (!defined('ACTION_SCHEDULER_RETENTION_PERIOD')) {
    define('ACTION_SCHEDULER_RETENTION_PERIOD', 3 * HOUR_IN_SECONDS);
}

add_filter('action_scheduler_retention_period', function() {
    return ACTION_SCHEDULER_RETENTION_PERIOD;
});

// ============================================================================
// LAYER 2: QUERY-LEVEL PROTECTION (memory safety)
// ============================================================================

/**
 * Add LIMIT to AS queries to prevent memory exhaustion
 * - Frontend: Strict limits, allow existence checks
 * - Backend: Reasonable limits
 */
function aieo_as_filter_query($query) {
    // Not an AS query
    if (strpos($query, 'actionscheduler_actions') === false &&
        strpos($query, 'actionscheduler_groups') === false) {
        return $query;
    }

    $normalized = trim(preg_replace('/\s+/', ' ', $query));

    // Not a SELECT - allow INSERT/UPDATE/DELETE
    if (stripos(trim($normalized), 'SELECT') !== 0) {
        return $query;
    }

    // Backend context - just add reasonable LIMIT
    if (aieo_as_request_needs_as()) {
        if (stripos($normalized, 'LIMIT') !== false) {
            return $query;
        }
        if (preg_match('/SELECT\s+(COUNT|count)\s*\(/i', $normalized)) {
            return $query;
        }
        return rtrim($query, "; \t\n\r") . ' LIMIT 1000';
    }

    // =========================================================================
    // FRONTEND/AJAX: Be selective about what we allow
    // =========================================================================
    global $wpdb;

    // ALLOW existence checks (SELECT 1 ... LIMIT 1) - these MUST work!
    if (preg_match('/SELECT\s+1\s+FROM/i', $normalized) && stripos($normalized, 'LIMIT 1') !== false) {
        return $query;
    }

    // ALLOW COUNT queries with specific hook
    if (preg_match('/SELECT\s+(COUNT|count)\s*\(/i', $normalized) && strpos($normalized, "hook =") !== false) {
        return $query;
    }

    // BLOCK bulk COUNT queries
    if (preg_match('/SELECT\s+(COUNT|count)\s*\(/i', $normalized)) {
        return "SELECT 0";
    }

    // ALLOW queries with LIMIT 1
    if (stripos($normalized, 'LIMIT 1') !== false) {
        return $query;
    }

    // Reduce large LIMITs
    if (preg_match('/LIMIT\s+(\d+)/i', $normalized, $matches)) {
        $limit = (int) $matches[1];
        if ($limit > 10) {
            return preg_replace('/LIMIT\s+\d+/i', 'LIMIT 10', $query);
        }
        return $query;
    }

    // BLOCK unlimited queries
    if (strpos($normalized, 'action_id') !== false) {
        return "SELECT 0 as action_id FROM {$wpdb->posts} WHERE 1=0";
    }
    if (strpos($normalized, 'group_id') !== false) {
        return "SELECT 0 as group_id FROM {$wpdb->posts} WHERE 1=0";
    }

    // Default: Add LIMIT 10
    if (stripos($normalized, 'LIMIT') === false) {
        return rtrim($query, "; \t\n\r") . ' LIMIT 10';
    }

    return $query;
}

add_filter('query', 'aieo_as_filter_query', 1);
add_filter('query', 'aieo_as_filter_query', -1000000);

// ============================================================================
// LAYER 3: EMERGENCY BLOCKER (blocks specific hooks from scheduling)
// ============================================================================

/**
 * Block INSERT statements for problematic hooks
 */
add_filter('query', function($query) {
    if (!aieo_as_is_emergency_blocker_enabled()) {
        return $query;
    }

    if (stripos(trim($query), 'INSERT') !== 0) {
        return $query;
    }

    global $wpdb;
    $table = $wpdb->prefix . 'actionscheduler_actions';
    if (strpos($query, $table) === false && strpos($query, 'actionscheduler_actions') === false) {
        return $query;
    }

    // Check blocked hooks
    foreach (AIEO_AS_BLOCKED_HOOKS as $hook) {
        if (strpos($query, "'{$hook}'") !== false || strpos($query, "\"{$hook}\"") !== false) {
            return "SELECT 1";
        }
    }

    return $query;
}, -999999);

/**
 * Block via AS filters (backup layer)
 * Returns existing action_id (not 0) so plugin diagnostics see a valid response.
 */
add_filter('action_scheduler_pre_schedule_recurring_action', function($pre, $timestamp, $interval, $hook) {
    if (!aieo_as_is_emergency_blocker_enabled()) {
        return $pre;
    }

    $should_block = in_array($hook, AIEO_AS_BLOCKED_HOOKS, true);
    if (!$should_block) {
        foreach (AIEO_AS_BLOCKED_PATTERNS as $pattern) {
            if (strpos($hook, $pattern) === 0) {
                $should_block = true;
                break;
            }
        }
    }

    if ($should_block) {
        global $wpdb;
        $existing_id = $wpdb->get_var($wpdb->prepare(
            "SELECT action_id FROM {$wpdb->prefix}actionscheduler_actions WHERE hook = %s AND status = 'pending' LIMIT 1",
            $hook
        ));
        return $existing_id ? (int) $existing_id : 1;
    }

    return $pre;
}, -999999, 4);

add_filter('action_scheduler_pre_schedule_single_action', function($pre, $timestamp, $hook) {
    if (!aieo_as_is_emergency_blocker_enabled()) {
        return $pre;
    }

    $should_block = in_array($hook, AIEO_AS_BLOCKED_HOOKS, true);
    if (!$should_block) {
        foreach (AIEO_AS_BLOCKED_PATTERNS as $pattern) {
            if (strpos($hook, $pattern) === 0) {
                $should_block = true;
                break;
            }
        }
    }

    if ($should_block) {
        global $wpdb;
        $existing_id = $wpdb->get_var($wpdb->prepare(
            "SELECT action_id FROM {$wpdb->prefix}actionscheduler_actions WHERE hook = %s AND status = 'pending' LIMIT 1",
            $hook
        ));
        return $existing_id ? (int) $existing_id : 1;
    }

    return $pre;
}, -999999, 3);

// ============================================================================
// LAYER 4: DISABLE AS ASYNC RUNNER ON FRONTEND/AJAX
// ============================================================================

add_filter('action_scheduler_allow_async_request_runner', function($allow) {
    if (aieo_as_request_needs_as()) {
        return $allow;
    }
    return false;
}, 1);

// ============================================================================
// LAYER 5: BLOCK AS ON ADMIN PAGES (except where needed)
// ============================================================================

$request_type = aieo_as_get_request_type();

if ($request_type === 'admin' && !aieo_as_request_needs_as()) {
    add_filter('action_scheduler_queue_runner_concurrent_batches', '__return_zero', 999);
    add_filter('action_scheduler_run_schedule', '__return_false', 999);

    // Block AS custom table queries on admin (except LIMIT 1 existence checks)
    add_filter('query', function($query) {
        if (stripos($query, 'SELECT') !== 0) {
            return $query;
        }

        if (strpos($query, 'actionscheduler_actions') === false &&
            strpos($query, 'actionscheduler_groups') === false) {
            return $query;
        }

        // Allow LIMIT 1 queries — these are fast existence checks used by
        // prevent-duplicate-scheduled-actions.php and plugin diagnostics
        $normalized = trim(preg_replace('/\s+/', ' ', $query));
        if (preg_match('/LIMIT\s+1\b/i', $normalized)) {
            return $query;
        }

        global $wpdb;
        return "SELECT 1 FROM {$wpdb->posts} WHERE 1=0";
    }, 1);
}

// ============================================================================
// LAYER 6: BLOCK AS ON ADMIN AJAX (except where needed)
// ============================================================================

if ($request_type === 'admin_ajax' && !aieo_as_request_needs_as()) {
    add_filter('action_scheduler_queue_runner_concurrent_batches', '__return_zero', 999);
    add_filter('action_scheduler_run_schedule', '__return_false', 999);
    add_filter('action_scheduler_allow_async_request_runner', '__return_false', 999);

    // Block AS queries on admin AJAX (except LIMIT 1 existence checks)
    add_filter('query', function($query) {
        if (stripos($query, 'SELECT') !== 0) {
            return $query;
        }

        if (strpos($query, "post_type = 'scheduled-action'") === false &&
            strpos($query, "post_type='scheduled-action'") === false &&
            strpos($query, 'actionscheduler_') === false) {
            return $query;
        }

        // Allow LIMIT 1 queries — these are fast existence checks used by
        // prevent-duplicate-scheduled-actions.php and plugin diagnostics
        $normalized = trim(preg_replace('/\s+/', ' ', $query));
        if (preg_match('/LIMIT\s+1\b/i', $normalized)) {
            return $query;
        }

        global $wpdb;
        return "SELECT 1 FROM {$wpdb->posts} WHERE 1=0";
    }, 1);

    add_filter('pre_get_posts', function($query) {
        if (isset($query->query['post_type']) && $query->query['post_type'] === 'scheduled-action') {
            $query->set('post__in', [0]);
        }
        return $query;
    }, 1);
}

// ============================================================================
// LAYER 7: BLOCK AS ON WC-AJAX (cart operations)
// ============================================================================

if ($request_type === 'wc_ajax') {
    add_filter('action_scheduler_allow_async_request_runner', '__return_false');
}

// ============================================================================
// LAYER 8: LIMIT AS QUERY ARGS (per_page)
// ============================================================================

add_filter('action_scheduler_query_actions_query_args', function($query) {
    if (!aieo_as_request_needs_as()) {
        $query['per_page'] = isset($query['per_page']) ? min($query['per_page'], 5) : 5;
    }
    return $query;
}, 1);

// ============================================================================
// LEGACY om_as_* FUNCTION ALIASES — back-compat for external code
// ============================================================================
// Wrappers that delegate to the new aieo_as_* implementations. Same
// rationale as the legacy constant aliases above — anything still calling
// `om_as_is_bypass_enabled()` etc. keeps working without us auditing every
// external mu-plugin in one go.
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Reason: legacy `om_as_*` aliases retained for external mu-plugins / old themes; the canonical implementations live in the aieo_as_* functions above.
if (!function_exists('om_as_is_bypass_enabled')) {
    function om_as_is_bypass_enabled()              { return aieo_as_is_bypass_enabled(); }
}
if (!function_exists('om_as_is_emergency_blocker_enabled')) {
    function om_as_is_emergency_blocker_enabled()   { return aieo_as_is_emergency_blocker_enabled(); }
}
if (!function_exists('om_as_get_request_type')) {
    function om_as_get_request_type()               { return aieo_as_get_request_type(); }
}
if (!function_exists('om_as_request_needs_as')) {
    function om_as_request_needs_as()               { return aieo_as_request_needs_as(); }
}
if (!function_exists('om_as_filter_query')) {
    function om_as_filter_query($query)             { return aieo_as_filter_query($query); }
}
// phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound
