deleteOldRedirectsCron($passId); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron maintenance failed', $e); } } } if (!function_exists('abj404_updateLogsHitsTableListener')) { /** @return void */ function abj404_updateLogsHitsTableListener() { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $logsRepo = abj_service('logs_repository'); $logsRepo->createRedirectsForViewHitsTable(); abj_service('stats_rollup')->continueRefresh(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron logs/hits refresh failed', $e); } } } if (!function_exists('abj404_repairCollationsListener')) { /** Run schema-wide plugin-table collation correction outside foreground requests, under the repair lock. @return void */ function abj404_repairCollationsListener(): void { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); (new ABJ_404_Solution_QueuedCollationRepair( abj_service('database_upgrades')->components()->collationDriftUpgrade()))->runScheduled(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron collation repair failed', $e); } } } if (!function_exists('abj404_adminTableRepairListener')) { /** Run the deferred admin-table index and denorm repair. @return void */ function abj404_adminTableRepairListener(): void { try { require_once(plugin_dir_path(ABJ404_FILE) . 'includes/Loader.php'); abj_service('database_upgrades')->components()->selfHealUpgrade()->repairAdminTables(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron admin table repair failed', $e); } } } if (!function_exists('abj404_legacySuggestionPageRecoveryListener')) { /** Continue a bounded legacy-page batch using the site's current saved destination. @return void */ function abj404_legacySuggestionPageRecoveryListener(): void { try { require_once(plugin_dir_path(ABJ404_FILE) . 'includes/Loader.php'); (new ABJ_404_Solution_LegacySuggestionPageRecovery())->continuePendingBatch(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron legacy suggestion page recovery failed', $e); } } } if (!function_exists('abj404_refreshStatusCountsListener')) { /** * Recompute one status-count cache outside the foreground admin request. * * @param mixed $scope */ function abj404_refreshStatusCountsListener($scope): void { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); if (!is_string($scope)) { throw new \InvalidArgumentException('Status-count refresh scope must be a string.'); } $viewReadService = abj_service('view_read_service'); $viewReadService->refreshStatusCounts($scope); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron status-count refresh failed', $e); } } } if (!function_exists('abj404_sendQueuedReportListener')) { /** * Cron handler for FeedbackTransport queued sends. Loads Loader.php so the * autoloader resolves ABJ_404_Solution_FeedbackTransport, then dispatches. * * @param string $uuid * @return void */ function abj404_sendQueuedReportListener($uuid) { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); ABJ_404_Solution_FeedbackTransport::handleQueuedSend(is_string($uuid) ? $uuid : ''); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron feedback transport failed', $e); } } } if (!function_exists('abj404_logsv2CanonicalUrlBackfillListener')) { /** Continue both canonical-url backlogs under one shared fifteen-second budget. @return void */ function abj404_logsv2CanonicalUrlBackfillListener(): void { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $dbUpgrades = ABJ_404_Solution_DatabaseUpgradesEtc::getInstance(); $backfill = $dbUpgrades->components()->canonicalUrlBackfillUpgrade(); // An in-flight plugin update can retain the previous component API. if (method_exists($backfill, 'continueCanonicalUrlBackfills')) { $backfill->continueCanonicalUrlBackfills(); } else { $backfill->backfillLogsv2CanonicalUrl(); } } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron canonical URL backfill continuation failed', $e); } } } if (!function_exists('abj404_logsv2MinLogIdBackfillListener')) { /** Continue the bounded min_log_id backfill after the foreground upgrade slice. @return void */ function abj404_logsv2MinLogIdBackfillListener(): void { try { require_once(plugin_dir_path(ABJ404_FILE) . 'includes/Loader.php'); ABJ_404_Solution_DatabaseUpgradesEtc::getInstance()->components() ->addedColumnBackfillUpgrade()->runScheduledMinLogIdBackfill(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron min_log_id backfill failed', $e); } } } if (!function_exists('abj404_redirectsDenormReconcileResumeListener')) { /** * Continue an existing nightly sweep under its normal per-invocation budget. * @param mixed $passId */ function abj404_redirectsDenormReconcileResumeListener($passId): void { if (!is_string($passId) || $passId === '') { return; } try { require_once(plugin_dir_path(ABJ404_FILE) . 'includes/Loader.php'); ABJ_404_Solution_DatabaseUpgradesEtc::getInstance()->components() ->dailyMaintenanceUpgrade()->resumeNightlyReconcilePass($passId); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron nightly reconcile continuation failed', $e); } } } if (!function_exists('abj404_redirectsDenormBackfillListener')) { /** * Drain of the main redirects denorm backlog (dest_for_view, * published_status, logshits, last_used) for an event on the legacy denorm * hook. Nothing in this release schedules that hook: the admin redirect-table * read queues abj404_admin_table_repair instead, whose listener runs this same * pass and whose queued event ordinary admin views also consume when WP-Cron * is stalled. Kept so an event queued by an earlier release still drains. The * sort-key drains run immediately after so Destination/URL sort gates can open * in the same deferred pass once the main source columns are populated. * * @return void */ function abj404_redirectsDenormBackfillListener() { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $denorm = ABJ_404_Solution_DatabaseUpgradesEtc::getInstance() ->components()->redirectsDenormBackfillUpgrade(); // One shared time budget across the three drains (see // runDeferredDenormBackfillPass): this cron listener and the // browser-triggered AJAX drain must not consume 3x the budget back to // back. $denorm->runDeferredDenormBackfillPass(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron redirects denorm backfill failed', $e); } } } if (!function_exists('abj404_redirectsSortKeyBackfillListener')) { /** * Drain of the redirects narrow sort-key columns (dest_sort_key / * url_sort_key). Light-path: the drains route through queryAndGetResults and * no-op on a missing/empty table, so a fresh-install or * deactivated-but-installed state degrades gracefully. * * @return void */ function abj404_redirectsSortKeyBackfillListener() { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $sortKey = ABJ_404_Solution_DatabaseUpgradesEtc::getInstance() ->components()->redirectsSortKeyBackfillUpgrade(); // Shared time budget across both sort-key drains (see // runDeferredSortKeyBackfillPass) so a browser-triggered or cron pass // is capped at one budget, not two back to back. $sortKey->runDeferredSortKeyBackfillPass(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron redirects sort-key backfill failed', $e); } } } if (!function_exists('abj404_updatePermalinkCacheListener')) { /** * @param int $maxExecutionTime * @param int $executionCount * @return void */ function abj404_updatePermalinkCacheListener($maxExecutionTime, $executionCount = 1) { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $permalinkCache = ABJ_404_Solution_PermalinkCache::getInstance(); $permalinkCache->updatePermalinkCache($maxExecutionTime, $executionCount); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron permalink cache update failed', $e); } } } if (!function_exists('abj404_rebuildNGramCacheListener')) { /** * @param int $offset * @return void */ function abj404_rebuildNGramCacheListener($offset = 0) { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $dbUpgrades = ABJ_404_Solution_DatabaseUpgradesEtc::getInstance(); $dbUpgrades->components()->nGramUpgrade()->rebuildNGramCacheAsync($offset); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron ngram cache rebuild failed', $e); } } } if (!function_exists('abj404_networkActivationListener')) { /** @return void */ function abj404_networkActivationListener() { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); ABJ_404_Solution_PluginLogicLifecycle::networkActivationCronHandler(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron network activation failed', $e); } } } if (!function_exists('abj404_networkActivationBackgroundListener')) { /** @return void */ function abj404_networkActivationBackgroundListener() { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $upgradesEtc = ABJ_404_Solution_DatabaseUpgradesEtc::getInstance(); $upgradesEtc->components()->multiSiteUpgrade()->processMultisiteActivationBatch(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron multisite activation batch failed', $e); } } } if (!function_exists('abj404_networkUpgradeBackgroundListener')) { /** @return void */ function abj404_networkUpgradeBackgroundListener() { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $upgradesEtc = ABJ_404_Solution_DatabaseUpgradesEtc::getInstance(); $upgradesEtc->components()->multiSiteUpgrade()->processMultisiteUpgradeBatch(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron multisite upgrade batch failed', $e); } } } if (!function_exists('abj404_sendDigestCronListener')) { /** @return void */ function abj404_sendDigestCronListener() { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $dao = ABJ_404_Solution_DataAccess::getInstance(); $logger = ABJ_404_Solution_Logging::getInstance(); $emailDigest = new ABJ_404_Solution_EmailDigest($dao, $logger); $emailDigest->onCronSendDigest(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron email digest failed', $e); } } } if (!function_exists('abj404_gscFetchCronListener')) { /** Nightly cron: fetch GSC data and cache it. @return void */ function abj404_gscFetchCronListener(): void { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $gscLogger = ABJ_404_Solution_Logging::getInstance(); $gsc = new ABJ_404_Solution_GoogleSearchConsole($gscLogger); $gsc->searchAnalytics()->fetchAndCacheGscData(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron GSC fetch failed', $e); } } } if (!function_exists('abj404_gscBackgroundRefreshListener')) { /** On-demand background refresh triggered when an admin views the Options tab with stale data. @return void */ function abj404_gscBackgroundRefreshListener(): void { try { require_once(plugin_dir_path( ABJ404_FILE ) . "includes/Loader.php"); $gscLogger = ABJ_404_Solution_Logging::getInstance(); $gsc = new ABJ_404_Solution_GoogleSearchConsole($gscLogger); $gsc->searchAnalytics()->fetchAndCacheGscData(); } catch (\Throwable $e) { abj404_logRuntimeWarning('Cron GSC background refresh failed', $e); } } }