PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / root-boot / AdminPageCallback.php

AdminPageCallback.php in 404 Solution trunk, at includes/root-boot/AdminPageCallback.php

118 lines 4.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit;
4 }
5
6 /**
7 * Main plugin admin-page callback and its one-time fatal-diagnostics notice.
8 *
9 * abj404_render_last_admin_fatal_notice() surfaces (once) a fatal captured
10 * during a previous admin request's shutdown. abj404_admin_page_callback() is
11 * the registered menu callback: it renders the View page, and on any failure
12 * (View class not loaded, render threw, or zero output) it shows a diagnostic
13 * instead of a blank page, falling back to the degraded admin page when the
14 * View class itself is unavailable.
15 *
16 * The menu registration (which points WordPress at this callback) is performed
17 * by ABJ_404_Solution_WordPress_Connector on a successful boot; this file only
18 * defines the callbacks. abj404_degraded_admin_page() (the fallback) lives in
19 * includes/root-boot/DegradedSupportRequest.php.
20 */
21 // allow-no-test-found: boot-time global admin-page callback wired via the WP admin menu in 404-solution.php; no same-named unit file. The blank-page fallback behavior of abj404_admin_page_callback is exercised in BlankPagePreventionTest (which references the callback directly).
22
23 if (!function_exists('abj404_admin_page_callback')) {
24 /**
25 * Show one-time admin fatal diagnostics captured during shutdown.
26 *
27 * @return void
28 */
29 function abj404_render_last_admin_fatal_notice() {
30 if (!abj404_current_user_is_plugin_admin()) {
31 return;
32 }
33
34 $fatalInfo = function_exists('get_transient') ? get_transient('abj404_admin_fatal') : false;
35 if ($fatalInfo === false && function_exists('get_option')) {
36 $fatalInfo = get_option('abj404_admin_fatal_fallback', false);
37 }
38 if (!is_array($fatalInfo) || empty($fatalInfo['message'])) {
39 return;
40 }
41
42 if (function_exists('delete_transient')) {
43 delete_transient('abj404_admin_fatal');
44 }
45 if (function_exists('delete_option')) {
46 delete_option('abj404_admin_fatal_fallback');
47 }
48
49 $pluginDir = defined('ABJ404_PATH') ? ABJ404_PATH : dirname(dirname(__DIR__)) . '/';
50 $fatalFileRaw = isset($fatalInfo['file']) && is_scalar($fatalInfo['file']) ? (string)$fatalInfo['file'] : '';
51 $fatalFile = $fatalFileRaw !== '' ? str_replace($pluginDir, '', $fatalFileRaw) : '(unknown file)';
52 $fatalLine = isset($fatalInfo['line']) && is_scalar($fatalInfo['line']) ? (int)$fatalInfo['line'] : 0;
53 $fatalMessage = isset($fatalInfo['message']) && is_scalar($fatalInfo['message']) ? (string)$fatalInfo['message'] : '';
54
55 echo '<div class="wrap">';
56 echo '<div class="notice notice-error">';
57 echo '<p><strong>404 Solution:</strong> A fatal error occurred while rendering the previous admin request.</p>';
58 echo '<details><summary>Show error details</summary>';
59 echo '<pre style="white-space:pre-wrap;word-break:break-all;max-width:100%;margin:6px 0;">' .
60 esc_html($fatalMessage . "\n" . $fatalFile . ':' . (string)$fatalLine) .
61 '</pre>';
62 echo '</details>';
63 echo '</div>';
64 echo '</div>';
65 }
66
67 /**
68 * Safe wrapper for the admin page callback. Falls back to the degraded
69 * page if the View class was not loaded during boot.
70 *
71 * @return void
72 */
73 function abj404_admin_page_callback() {
74 abj404_render_last_admin_fatal_notice();
75
76 // The false parameter avoids triggering the autoloader. If View was not
77 // loaded during boot, we don't want to attempt loading it again here.
78 if (class_exists('ABJ_404_Solution_View', false)) {
79 ob_start();
80 $renderError = null;
81 try {
82 ABJ_404_Solution_View::handleMainAdminPageActionAndDisplay();
83 } catch (\Throwable $e) {
84 $renderError = $e;
85 abj404_logRuntimeWarning('Admin page rendering failed', $e);
86 }
87 $output = ob_get_clean();
88
89 if ($renderError !== null) {
90 echo '<div class="wrap">';
91 echo '<div class="notice notice-error">';
92 echo '<p><strong>404 Solution:</strong> An error occurred while rendering this page.</p>';
93 echo '<details><summary>Show error details</summary>';
94 echo '<pre style="white-space:pre-wrap;word-break:break-all;max-width:100%;margin:6px 0;">' . esc_html($renderError->getMessage() . "\n" . $renderError->getTraceAsString()) . '</pre>';
95 echo '</details>';
96 echo '</div>';
97 echo '</div>';
98 } elseif ($output === '' || $output === false) {
99 // The View class was loaded and didn't throw, but produced zero output.
100 // Show a diagnostic instead of a blank page.
101 echo '<div class="wrap">';
102 echo '<h1>404 Solution</h1>';
103 echo '<div class="notice notice-error"><p>';
104 echo '<strong>This page produced no output.</strong> ';
105 echo 'This can happen when a required dependency failed to initialize or a template file is missing.';
106 echo '</p><p>';
107 echo 'Try deactivating and reactivating the plugin. If the problem persists, ';
108 echo 'delete the plugin and reinstall it from the WordPress plugin directory.';
109 echo '</p></div></div>';
110 } else {
111 echo $output;
112 }
113 } else {
114 abj404_degraded_admin_page();
115 }
116 }
117 }
118