| 1 |
<?php |
| 2 |
|
| 3 |
|
| 4 |
if (!defined('ABSPATH')) { |
| 5 |
exit; |
| 6 |
} |
| 7 |
|
| 8 |
/* |
| 9 |
Plugin Name: 404 Solution |
| 10 |
Plugin URI: https://www.ajexperience.com/404-solution/ |
| 11 |
Description: The smartest 404 plugin - uses intelligent matching and spell-checking to find what visitors were actually looking for, not just redirect to homepage |
| 12 |
Author: Aaron J |
| 13 |
Author URI: https://www.ajexperience.com/404-solution/ |
| 14 |
|
| 15 |
Version: 4.3.5 |
| 16 |
Requires at least: 5.0 |
| 17 |
Requires PHP: 7.4 |
| 18 |
|
| 19 |
License: GPL-3.0-or-later |
| 20 |
License URI: https://www.gnu.org/licenses/gpl-3.0.html |
| 21 |
Domain Path: /languages |
| 22 |
Text Domain: 404-solution |
| 23 |
|
| 24 |
This program is free software; you can redistribute it and/or modify |
| 25 |
it under the terms of the GNU General Public License as published by |
| 26 |
the Free Software Foundation; either version 2 of the License, or |
| 27 |
(at your option) any later version. |
| 28 |
|
| 29 |
This program is distributed in the hope that it will be useful, |
| 30 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 31 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 32 |
GNU General Public License for more details. |
| 33 |
|
| 34 |
You should have received a copy of the GNU General Public License |
| 35 |
along with this program; if not, write to the Free Software |
| 36 |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 37 |
*/ |
| 38 |
|
| 39 |
// Guard constant definitions so unit tests (and unusual loaders) can define them first. |
| 40 |
if (!defined('ABJ404_PP')) { |
| 41 |
define('ABJ404_PP', 'abj404_solution'); |
| 42 |
} |
| 43 |
if (!defined('ABJ404_FILE')) { |
| 44 |
define('ABJ404_FILE', __FILE__); |
| 45 |
} |
| 46 |
if (!defined('ABJ404_PATH')) { |
| 47 |
define('ABJ404_PATH', plugin_dir_path(ABJ404_FILE)); |
| 48 |
} |
| 49 |
// Content-addressed release marker compiled into the earliest boot file. |
| 50 |
// DiagnosticModuleManifestTest recomputes it from every covered PHP module. |
| 51 |
if (!defined('ABJ404_DIAGNOSTIC_BUILD_ID')) { |
| 52 |
define('ABJ404_DIAGNOSTIC_BUILD_ID', '1675af0a29435f25cc1afbaef80703108620b1f8'); |
| 53 |
} |
| 54 |
|
| 55 |
// The plugin version is read from this file's own header (single source of |
| 56 |
// truth) and is defined HERE rather than in Loader.php because the |
| 57 |
// post-upgrade opcache guard below needs it before any other plugin file |
| 58 |
// loads. Loader.php keeps its own guarded define for loaders that reach it |
| 59 |
// without going through this entry point; it becomes a no-op in normal boots. |
| 60 |
// get_file_data() reads from disk, so the version is correct even when this |
| 61 |
// entry point is itself being served from stale bytecode. |
| 62 |
if (!defined('ABJ404_VERSION') && function_exists('get_file_data')) { |
| 63 |
$__abj404_header = get_file_data(ABJ404_FILE, array('Version' => 'Version')); |
| 64 |
define('ABJ404_VERSION', isset($__abj404_header['Version']) ? $__abj404_header['Version'] : ''); |
| 65 |
unset($__abj404_header); |
| 66 |
} |
| 67 |
|
| 68 |
// POST-UPGRADE OPCACHE GUARD -- must stay the FIRST plugin file required, ahead |
| 69 |
// of every other require and ahead of spl_autoload_register() below. |
| 70 |
// |
| 71 |
// PHP revalidates cached bytecode per file on its own schedule, so for a few |
| 72 |
// seconds after an update the class graph can be MIXED: a file at a path that |
| 73 |
// is new in this release compiles fresh from disk while a file whose path did |
| 74 |
// not change is still the previous release's bytecode. When a parent and its |
| 75 |
// subclass land on opposite sides of that split and a signature changed, PHP |
| 76 |
// raises an uncatchable E_ERROR while LINKING them -- which is exactly what |
| 77 |
// killed requests on a real 4.2.0 -> 4.3.1 upgrade (see the file header of |
| 78 |
// includes/root-boot/OpcacheUpgradeGuard.php for the incident detail). |
| 79 |
// |
| 80 |
// Because the fatal happens at class-linking time, the flush is only useful if |
| 81 |
// it runs before the autoloader can link anything. Anything later (the old |
| 82 |
// PluginLogicVersionUpgrader::invalidateOpcacheForCriticalFiles(), which ran |
| 83 |
// inside the booted plugin) is structurally incapable of saving the request |
| 84 |
// that dies. The call is gated on a persisted version stamp -- the |
| 85 |
// `abj404_opcache_version` option, which doubles as the support-visible record |
| 86 |
// of when this last ran -- so the work happens once per upgrade, not once per |
| 87 |
// request. There is no logger this early in the boot, by construction. |
| 88 |
require_once __DIR__ . '/includes/root-boot/OpcacheUpgradeGuard.php'; |
| 89 |
abj404_opcache_refresh_after_upgrade(); |
| 90 |
|
| 91 |
require_once __DIR__ . '/includes/core/PhpErrorLogFallback.php'; |
| 92 |
if (!defined('ABJ404_SHORTCODE_NAME')) { |
| 93 |
define('ABJ404_SHORTCODE_NAME', 'abj404_solution_page_suggestions'); |
| 94 |
} |
| 95 |
if (!isset($GLOBALS['abj404_display_errors'])) { |
| 96 |
$GLOBALS['abj404_display_errors'] = false; |
| 97 |
} |
| 98 |
|
| 99 |
// Boot state: tracks whether the plugin loaded successfully. |
| 100 |
// If a required file is missing or Loader.php fails, these let us show |
| 101 |
// a degraded admin page instead of a fatal error. |
| 102 |
$GLOBALS['abj404_boot_ok'] = false; |
| 103 |
$GLOBALS['abj404_missing_files'] = array(); |
| 104 |
$GLOBALS['abj404_boot_error'] = ''; |
| 105 |
|
| 106 |
// Early settings and path helpers (abj404_getUploadsDir, abj404_get_settings_options). |
| 107 |
// Required before Loader.php because boot-time paths call them before the service |
| 108 |
// container is available. Defined in includes/root-boot/EarlySettingsAccess.php. |
| 109 |
require_once __DIR__ . '/includes/root-boot/EarlySettingsAccess.php'; |
| 110 |
|
| 111 |
// Debug whitelist - only includes localhost/development environments by default |
| 112 |
// WARNING: Only add trusted domains to this list. External domains could be a security risk. |
| 113 |
// This list is used to enable detailed error logging for debugging purposes. |
| 114 |
$GLOBALS['abj404_whitelist'] = array('127.0.0.1', '::1', 'localhost'); |
| 115 |
|
| 116 |
// Allow filtering the whitelist for advanced users who need to add custom domains |
| 117 |
// Usage: add_filter('abj404_debug_whitelist', function($whitelist) { $whitelist[] = 'yourdomain.com'; return $whitelist; }); |
| 118 |
if (has_filter('abj404_debug_whitelist')) { |
| 119 |
$GLOBALS['abj404_whitelist'] = apply_filters('abj404_debug_whitelist', $GLOBALS['abj404_whitelist']); |
| 120 |
} |
| 121 |
|
| 122 |
// The class autoloader (abj404_autoloader) is defined in |
| 123 |
// includes/root-boot/Autoloader.php. Required FIRST (plain require, before any |
| 124 |
// class use) so the function is available to register below; it loads its |
| 125 |
// class->collaborator dependency map from the external data file |
| 126 |
// includes/data/autoloader-trait-dependencies.php. |
| 127 |
require_once __DIR__ . '/includes/root-boot/Autoloader.php'; |
| 128 |
spl_autoload_register('abj404_autoloader'); |
| 129 |
|
| 130 |
// Boot lifecycle waypoint checkpoints (Bruno timeout cause matrix, gap G3): |
| 131 |
// boot_delta_ms used to be the ONLY boot measurement, and it was written at |
| 132 |
// trace construction -- AFTER auth and the rate limiter -- so a slow boot and |
| 133 |
// a slow auth/DB path were indistinguishable. These checkpoints localize a |
| 134 |
// slow boot to a phase instead of a total. Gated to our own table-AJAX and |
| 135 |
// canary-ladder requests only (see |
| 136 |
// ABJ_404_Solution_AjaxDiagnosticRequestPolicy::bootWaypointRequestId()); the frontend |
| 137 |
// 404 path is hot and must never pay this write cost. |
| 138 |
// |
| 139 |
// 'muplugins_loaded' is not separately hooked here: WordPress fires that |
| 140 |
// action (wp-settings.php, do_action('muplugins_loaded')) BEFORE a regular |
| 141 |
// (non-mu) plugin's file is even require()'d, so a normal plugin can never |
| 142 |
// observe it firing -- registering a callback for it here would silently |
| 143 |
// never run. This waypoint, the earliest point our own code can reach the |
| 144 |
// checkpoint logger, is therefore the honest measurement of that entire |
| 145 |
// pre-active-plugin window: mu-plugins, every listener on muplugins_loaded, |
| 146 |
// and any plugin that loads ahead of us in the active-plugins list. |
| 147 |
ABJ_404_Solution_BootWaypointRecorder::record('boot_plugin_entry', array( |
| 148 |
'module' => '404-solution', |
| 149 |
'path' => __FILE__, |
| 150 |
'build_id' => ABJ404_DIAGNOSTIC_BUILD_ID, |
| 151 |
)); |
| 152 |
add_action('plugins_loaded', static function () { |
| 153 |
ABJ_404_Solution_BootWaypointRecorder::record('plugins_loaded', array( |
| 154 |
'module' => '404-solution', |
| 155 |
'path' => __FILE__, |
| 156 |
'build_id' => ABJ404_DIAGNOSTIC_BUILD_ID, |
| 157 |
)); |
| 158 |
// Same-site concurrency census (Bruno timeout cause matrix, gap GC). |
| 159 |
// Registered here rather than at the plugin file's first line because it |
| 160 |
// needs the service container, which Loader.php builds further down this |
| 161 |
// file; 'plugins_loaded' is the earliest hook that is guaranteed to run |
| 162 |
// after that for EVERY request, including the wp-cron loopbacks and the |
| 163 |
// other plugins' admin-ajax polls whose contention is the thing being |
| 164 |
// counted. The census itself decides which requests are in scope (see |
| 165 |
// ABJ_404_Solution_SameSiteRequestCensus::channelForThisRequest); the hot |
| 166 |
// front-end 404 path is not one of them. |
| 167 |
// |
| 168 |
// class_exists() first (Defensive Coding #1): the safe autoloader returns |
| 169 |
// SILENTLY for a missing class file, so an install with a corrupt or |
| 170 |
// partially-updated plugin directory would turn this diagnostic into a |
| 171 |
// fatal on every admin request. A missing diagnostic is the correct |
| 172 |
// degradation; a broken admin screen is not. |
| 173 |
if (class_exists('ABJ_404_Solution_SameSiteRequestCensus')) { |
| 174 |
ABJ_404_Solution_SameSiteRequestCensus::join(); |
| 175 |
} |
| 176 |
}, PHP_INT_MIN); |
| 177 |
add_action('init', static function () { |
| 178 |
ABJ_404_Solution_BootWaypointRecorder::record('init', array( |
| 179 |
'module' => '404-solution', |
| 180 |
'path' => __FILE__, |
| 181 |
'build_id' => ABJ404_DIAGNOSTIC_BUILD_ID, |
| 182 |
)); |
| 183 |
}, PHP_INT_MIN); |
| 184 |
add_action('admin_init', static function () { |
| 185 |
ABJ_404_Solution_BootWaypointRecorder::record('admin_init', array( |
| 186 |
'module' => '404-solution', |
| 187 |
'path' => __FILE__, |
| 188 |
'build_id' => ABJ404_DIAGNOSTIC_BUILD_ID, |
| 189 |
)); |
| 190 |
}, PHP_INT_MIN); |
| 191 |
|
| 192 |
// Root-boot procedural functions. Each file only DEFINES functions (the |
| 193 |
// add_action/add_filter/add_shortcode registrations stay below in this file). |
| 194 |
// Requiring them here, right after the autoloader is registered, guarantees |
| 195 |
// every function is defined before any top-level executable statement that |
| 196 |
// references it runs. |
| 197 |
require_once __DIR__ . '/includes/root-boot/RuntimeHelpers.php'; |
| 198 |
require_once __DIR__ . '/includes/root-boot/RequestBenchmark.php'; |
| 199 |
require_once __DIR__ . '/includes/root-boot/BootGuard.php'; |
| 200 |
require_once __DIR__ . '/includes/root-boot/ShortcodeAndIntegrity.php'; |
| 201 |
require_once __DIR__ . '/includes/root-boot/DegradedSupportRequest.php'; |
| 202 |
require_once __DIR__ . '/includes/root-boot/CronListeners.php'; |
| 203 |
require_once __DIR__ . '/includes/root-boot/LocaleAndFrontendOptions.php'; |
| 204 |
require_once __DIR__ . '/includes/root-boot/AdminNotices.php'; |
| 205 |
require_once __DIR__ . '/includes/root-boot/LocalDebugDiagnostics.php'; |
| 206 |
require_once __DIR__ . '/includes/root-boot/AdminInitHandlers.php'; |
| 207 |
require_once __DIR__ . '/includes/root-boot/AdminPageCallback.php'; |
| 208 |
require_once __DIR__ . '/includes/root-boot/Frontend404Listener.php'; |
| 209 |
|
| 210 |
|
| 211 |
add_action('doing_it_wrong_run', function($function_name, $message, $version) { |
| 212 |
if (strpos($message, '404-solution') !== false && |
| 213 |
$function_name == '_load_textdomain_just_in_time') { |
| 214 |
|
| 215 |
try { |
| 216 |
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); |
| 217 |
|
| 218 |
// Prepare the plugin path from ABJ404_FILE |
| 219 |
$pluginPath = trailingslashit(plugin_dir_path(ABJ404_FILE)); // e.g., /var/www/html/wp-content/plugins/404-solution/ |
| 220 |
|
| 221 |
$logMessage = ''; |
| 222 |
$isOurPlugin = false; |
| 223 |
|
| 224 |
foreach ($backtrace as $index => $frame) { |
| 225 |
$file = isset($frame['file']) && is_string($frame['file']) ? $frame['file'] : '[internal function]'; |
| 226 |
$line = isset($frame['line']) && is_scalar($frame['line']) ? (string)$frame['line'] : ''; |
| 227 |
$func = $frame['function']; |
| 228 |
|
| 229 |
if (!$isOurPlugin && $file !== '[internal function]' && strpos($file, $pluginPath) !== false) { |
| 230 |
$isOurPlugin = true; |
| 231 |
} |
| 232 |
|
| 233 |
$logMessage .= "#$index $func at [$file:$line]\n"; |
| 234 |
} |
| 235 |
|
| 236 |
if ($isOurPlugin) { |
| 237 |
$header = "=== Detected Early Translation ===\n" . |
| 238 |
"Function: $function_name\n" . |
| 239 |
"Message: $message\n" . |
| 240 |
"Version: $version\n"; |
| 241 |
|
| 242 |
if (!isset($GLOBALS['abj404_pending_errors']) || !is_array($GLOBALS['abj404_pending_errors'])) { |
| 243 |
$GLOBALS['abj404_pending_errors'] = []; |
| 244 |
} |
| 245 |
$GLOBALS['abj404_pending_errors'][] = $header . $logMessage; |
| 246 |
} |
| 247 |
|
| 248 |
} catch (Throwable $e) { |
| 249 |
abj404_logRuntimeWarning('Failed to capture early translation stack trace', $e); |
| 250 |
} |
| 251 |
} |
| 252 |
}, 10, 3); |
| 253 |
|
| 254 |
// shortcode (abj404_shortCodeListener is defined in |
| 255 |
// includes/root-boot/ShortcodeAndIntegrity.php). |
| 256 |
add_shortcode(ABJ404_SHORTCODE_NAME, 'abj404_shortCodeListener'); |
| 257 |
|
| 258 |
// Benchmark instrumentation functions are defined in |
| 259 |
// includes/root-boot/RequestBenchmark.php. Start the per-request timer now and, |
| 260 |
// when benchmarking is active, register the response-header emitter. These |
| 261 |
// executable statements must run unconditionally (they previously sat inside the |
| 262 |
// shortcode-listener function_exists guard). |
| 263 |
abj404_benchmark_bootstrap_start(); |
| 264 |
if (abj404_is_benchmark_request()) { |
| 265 |
add_action('send_headers', 'abj404_benchmark_emit_headers', PHP_INT_MAX); |
| 266 |
} |
| 267 |
|
| 268 |
// Boot-failure shutdown handler (abj404_boot_shutdown_handler) is defined in |
| 269 |
// includes/root-boot/BootGuard.php. It captures compile/parse fatals in plugin |
| 270 |
// files so the degraded admin page can show them on the next request. Registered |
| 271 |
// here as part of the boot sequence. |
| 272 |
register_shutdown_function('abj404_boot_shutdown_handler'); |
| 273 |
|
| 274 |
// Always load Loader.php to ensure plugin constants (ABJ404_TYPE_404_DISPLAYED, |
| 275 |
// ABJ404_STATUS_MANUAL, etc.) are defined in all contexts: admin, REST API, WP-CLI |
| 276 |
// eval, and template_redirect. Without this, direct calls to plugin classes via |
| 277 |
// wp eval fail with "Undefined constant" errors because Loader.php was previously |
| 278 |
// only loaded inside is_admin(), leaving WP-CLI and other non-admin contexts |
| 279 |
// without the constants they need. |
| 280 |
$__abj404_loader_path = plugin_dir_path( __FILE__ ) . "includes/Loader.php"; |
| 281 |
if (file_exists($__abj404_loader_path)) { |
| 282 |
try { |
| 283 |
require_once($__abj404_loader_path); |
| 284 |
$GLOBALS['abj404_boot_ok'] = true; |
| 285 |
// Clear any stale boot fatal transient from a previous failed load. |
| 286 |
if (function_exists('delete_transient')) { |
| 287 |
delete_transient('abj404_boot_fatal'); |
| 288 |
} |
| 289 |
} catch (\Throwable $e) { |
| 290 |
$GLOBALS['abj404_boot_ok'] = false; |
| 291 |
$GLOBALS['abj404_boot_error'] = $e->getMessage(); |
| 292 |
abj404_logRuntimeWarning('Boot failed while loading Loader.php', $e); |
| 293 |
} |
| 294 |
} else { |
| 295 |
$GLOBALS['abj404_boot_ok'] = false; |
| 296 |
$GLOBALS['abj404_missing_files'][] = $__abj404_loader_path; |
| 297 |
$GLOBALS['abj404_boot_error'] = 'Loader.php is missing.'; |
| 298 |
} |
| 299 |
unset($__abj404_loader_path); |
| 300 |
|
| 301 |
if ($GLOBALS['abj404_boot_ok']) { |
| 302 |
// Lifecycle hooks must be registered in every load context. WP-CLI loads |
| 303 |
// the plugin outside wp-admin before `wp plugin activate`, then fires the |
| 304 |
// callback registered during that load. Admin-only hooks remain guarded |
| 305 |
// inside WordPressHookRegistrar::registerAdminHooks(). |
| 306 |
try { |
| 307 |
ABJ_404_Solution_WordPress_Connector::init(); |
| 308 |
if (is_admin()) { |
| 309 |
ABJ_404_Solution_AjaxAdminEndpointRegistrar::register(); |
| 310 |
} |
| 311 |
} catch (\Throwable $e) { |
| 312 |
// init() failed. On admin requests, register the degraded page so the |
| 313 |
// user still gets the original error detail instead of a missing menu. |
| 314 |
$GLOBALS['abj404_boot_ok'] = false; |
| 315 |
$GLOBALS['abj404_boot_error'] = 'Plugin initialization failed: ' . $e->getMessage(); |
| 316 |
abj404_logRuntimeWarning('Plugin initialization failed', $e); |
| 317 |
if (is_admin()) { |
| 318 |
add_action('admin_menu', 'abj404_degraded_admin_menu'); |
| 319 |
add_action('admin_notices', 'abj404_degraded_admin_notice'); |
| 320 |
} |
| 321 |
} |
| 322 |
|
| 323 |
// REST API: deferred to rest_api_init so DataAccess/PluginLogic are only loaded on actual REST requests. |
| 324 |
add_action('rest_api_init', function() { |
| 325 |
try { |
| 326 |
$dao = ABJ_404_Solution_DataAccess::getInstance(); |
| 327 |
$logic = ABJ_404_Solution_PluginLogic::getInstance(); |
| 328 |
$restController = new ABJ_404_Solution_RestApiController($dao, $logic); |
| 329 |
$restController->registerRoutes(); |
| 330 |
} catch (\Throwable $e) { |
| 331 |
// rest_api_init is a shared WordPress hook every plugin's REST |
| 332 |
// routes register on; an uncaught error here aborts the whole |
| 333 |
// request and would also block other plugins' route |
| 334 |
// registration, not just ours. A transient failure resolving |
| 335 |
// this plugin's services (the VRMU incident: a class |
| 336 |
// momentarily missing during a plugin self-update racing a |
| 337 |
// live request) must degrade to "skip registering our routes |
| 338 |
// this request" instead. |
| 339 |
abj404_logRuntimeWarning('rest_api_init: route registration failed', $e); |
| 340 |
} |
| 341 |
}); |
| 342 |
|
| 343 |
// WP-CLI commands. |
| 344 |
if (defined('WP_CLI') && WP_CLI) { |
| 345 |
add_action('init', function() { |
| 346 |
\WP_CLI::add_command('abj404', 'ABJ_404_Solution_WPCLICommands'); |
| 347 |
}, 1); |
| 348 |
} |
| 349 |
} elseif (function_exists('is_admin') && is_admin()) { |
| 350 |
// Boot failed. Register degraded admin page so the admin sees instructions |
| 351 |
// instead of a white screen or missing menu item. |
| 352 |
add_action('admin_menu', 'abj404_degraded_admin_menu'); |
| 353 |
add_action('admin_notices', 'abj404_degraded_admin_notice'); |
| 354 |
// Best-effort: wire the support-request flow even on the degraded |
| 355 |
// boot path so an admin who lands here can still send a debug report. |
| 356 |
// This is the most valuable placement for the button, because the user |
| 357 |
// often cannot reach the normal plugin UI from this screen. Deferred |
| 358 |
// to plugins_loaded so the function (defined in DegradedSupportRequest.php) |
| 359 |
// is available regardless of source-order. |
| 360 |
if (function_exists('add_action')) { |
| 361 |
add_action('plugins_loaded', 'abj404_degraded_register_support_request'); |
| 362 |
} |
| 363 |
} |
| 364 |
|
| 365 |
// --- Degraded-mode and admin-page callbacks --- |
| 366 |
// abj404_degraded_register_support_request, abj404_degraded_admin_menu, |
| 367 |
// abj404_degraded_admin_notice, and abj404_degraded_admin_page are defined in |
| 368 |
// includes/root-boot/DegradedSupportRequest.php. abj404_admin_page_callback and |
| 369 |
// abj404_render_last_admin_fatal_notice are defined in |
| 370 |
// includes/root-boot/AdminPageCallback.php. |
| 371 |
|
| 372 |
// ---- |
| 373 |
// get the plugin priority to use before adding the template_redirect action. |
| 374 |
$__abj404_options = abj404_get_settings_options(); |
| 375 |
$__abj404_redirect_priority_raw = isset($__abj404_options['template_redirect_priority']) && is_scalar($__abj404_options['template_redirect_priority']) ? $__abj404_options['template_redirect_priority'] : 9; |
| 376 |
$__abj404_template_redirect_priority = absint($__abj404_redirect_priority_raw); |
| 377 |
$__abj404_redirect_all = isset($__abj404_options['redirect_all_requests']) && is_scalar($__abj404_options['redirect_all_requests']) ? (string)$__abj404_options['redirect_all_requests'] : ''; |
| 378 |
$__abj404_update_suggest = isset($__abj404_options['update_suggest_url']) && is_scalar($__abj404_options['update_suggest_url']) ? (string)$__abj404_options['update_suggest_url'] : ''; |
| 379 |
$GLOBALS['abj404_frontend_runtime_flags'] = array( |
| 380 |
'redirect_all_requests' => ($__abj404_redirect_all === '1'), |
| 381 |
'update_suggest_url' => ($__abj404_update_suggest === '1'), |
| 382 |
); |
| 383 |
$__abj404_lang_override = isset($__abj404_options['plugin_language_override']) && is_string($__abj404_options['plugin_language_override']) ? $__abj404_options['plugin_language_override'] : ''; |
| 384 |
$GLOBALS['abj404_plugin_language_override'] = $__abj404_lang_override; |
| 385 |
|
| 386 |
// abj404_404listener is defined in includes/root-boot/Frontend404Listener.php. |
| 387 |
add_action('template_redirect', 'abj404_404listener', $__abj404_template_redirect_priority); |
| 388 |
|
| 389 |
unset($__abj404_options); |
| 390 |
unset($__abj404_template_redirect_priority); |
| 391 |
abj404_benchmark_mark_bootstrap_done(); |
| 392 |
// --- |
| 393 |
|
| 394 |
// Cron action registrations. The heavy-prologue cron listeners |
| 395 |
// (abj404_dailyMaintenanceCronJobListener, abj404_updateLogsHitsTableListener, |
| 396 |
// abj404_sendQueuedReportListener) and the |
| 397 |
// secondary cron listeners are all defined in includes/root-boot/CronListeners.php; |
| 398 |
// only their add_action wiring lives here so the entry-point self-heal audit and |
| 399 |
// the deactivate round-trip tests can discover the hook names in one place. |
| 400 |
add_action('abj404_cleanupCronAction', 'abj404_dailyMaintenanceCronJobListener'); |
| 401 |
add_action('abj404_updateLogsHitsTableAction', 'abj404_updateLogsHitsTableListener'); |
| 402 |
add_action('abj404_refresh_status_counts', 'abj404_refreshStatusCountsListener', 10, 1); |
| 403 |
add_action('abj404_repair_collations', 'abj404_repairCollationsListener'); |
| 404 |
add_action('abj404_logsv2_canonical_backfill', 'abj404_logsv2CanonicalUrlBackfillListener'); |
| 405 |
add_action('abj404_redirects_denorm_backfill', 'abj404_redirectsDenormBackfillListener'); |
| 406 |
add_action('abj404_redirects_sort_key_backfill', 'abj404_redirectsSortKeyBackfillListener'); |
| 407 |
add_action('abj404_updatePermalinkCacheAction', 'abj404_updatePermalinkCacheListener', 10, 2); |
| 408 |
add_action('abj404_send_digest', 'abj404_sendDigestCronListener'); |
| 409 |
add_action('abj404_send_queued_report', 'abj404_sendQueuedReportListener', 10, 1); |
| 410 |
add_action('abj404_rebuild_ngram_cache_hook', 'abj404_rebuildNGramCacheListener', 10, 1); |
| 411 |
add_action('abj404_network_activation_hook', 'abj404_networkActivationListener'); |
| 412 |
add_action('abj404_network_activation_background', 'abj404_networkActivationBackgroundListener'); |
| 413 |
add_action('abj404_network_upgrade_background', 'abj404_networkUpgradeBackgroundListener'); |
| 414 |
add_action('abj404_gsc_fetch_cron', 'abj404_gscFetchCronListener'); |
| 415 |
add_action('abj404_gsc_background_refresh', 'abj404_gscBackgroundRefreshListener'); |
| 416 |
|
| 417 |
// abj404_override_plugin_locale is defined in |
| 418 |
// includes/root-boot/LocaleAndFrontendOptions.php (along with |
| 419 |
// abj404_is_redirect_all_requests_enabled). |
| 420 |
add_filter('plugin_locale', 'abj404_override_plugin_locale', 999, 2); |
| 421 |
|
| 422 |
// abj404_show_runtime_integrity_notice and abj404_show_plugin_db_notice are |
| 423 |
// defined in includes/root-boot/AdminNotices.php. |
| 424 |
add_action('admin_notices', 'abj404_show_runtime_integrity_notice'); |
| 425 |
add_action('admin_notices', 'abj404_show_plugin_db_notice'); |
| 426 |
|
| 427 |
// abj404_is_local_debug_host, abj404_get_simulated_db_latency_ms, and |
| 428 |
// abj404_show_diagnostic_latency_notice are defined in |
| 429 |
// includes/root-boot/LocalDebugDiagnostics.php. |
| 430 |
|
| 431 |
// abj404_load_textdomain_if_needed, abj404_maybe_refresh_runtime_integrity_cache, |
| 432 |
// and abj404_loadSomethingWhenWordPressIsReady are defined in |
| 433 |
// includes/root-boot/AdminInitHandlers.php. |
| 434 |
add_action('admin_init', 'abj404_loadSomethingWhenWordPressIsReady'); |
| 435 |
|