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 / admin / AdminRuntimeErrorNotice.php

AdminRuntimeErrorNotice.php in 404 Solution trunk, at includes/admin/AdminRuntimeErrorNotice.php

87 lines 3.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Captures runtime exceptions thrown from admin-page WordPress hooks and
9 * renders a one-time admin notice instead of letting the page blank out.
10 *
11 * Admin hook callbacks (admin_head, asset enqueue, etc.) wrap their bodies in
12 * try/catch and forward any Throwable here via {@see reportAdminRuntimeError()}.
13 * The summary is buffered in static state for the current request and also
14 * persisted to a short-lived transient so an error raised during one admin
15 * request can still surface on the next page load. {@see echoAdminRuntimeErrorNotice()}
16 * drains both sources and prints the notice.
17 *
18 * This is presentation/infrastructure for admin error visibility only: it makes
19 * no business decisions and owns no domain data.
20 */
21 class ABJ_404_Solution_AdminRuntimeErrorNotice {
22
23 /** @var array<int, string> */
24 private static $adminRuntimeErrors = array();
25
26 /**
27 * Persist and queue an admin runtime error so users see a notice instead of a blank page.
28 *
29 * @param string $hookName
30 * @param Throwable $e
31 * @return void
32 */
33 public static function reportAdminRuntimeError(string $hookName, Throwable $e): void {
34 $summary = sprintf('[%s] %s', $hookName, $e->getMessage());
35 self::$adminRuntimeErrors[] = $summary;
36
37 try {
38 $logger = abj_service('logging');
39 $logger->errorMessage('Admin runtime exception in ' . $hookName . ': ' . $e->getMessage());
40 } catch (Throwable $ignored) {
41 abj404_logPhpFallback(
42 'service-resolution-fallback',
43 'admin runtime exception in ' . $hookName . ': ' . $e->getMessage()
44 );
45 }
46
47 if (function_exists('set_transient')) {
48 // allow-cache-empty: runtime-error notice summary is generated locally and intentionally persisted as-is.
49 set_transient('abj404_admin_runtime_error', $summary, 300);
50 }
51 }
52
53 /**
54 * Echo one-time admin runtime errors captured from earlier hooks in this request (or previous request).
55 *
56 * @return void
57 */
58 public static function echoAdminRuntimeErrorNotice(): void {
59 $errors = self::$adminRuntimeErrors;
60 self::$adminRuntimeErrors = array();
61
62 if (function_exists('get_transient')) {
63 $saved = get_transient('abj404_admin_runtime_error');
64 if (is_string($saved) && $saved !== '') {
65 $errors[] = $saved;
66 delete_transient('abj404_admin_runtime_error');
67 }
68 }
69
70 if (empty($errors)) {
71 return;
72 }
73
74 $message = implode("\n", array_unique(array_filter($errors)));
75 $template = ABJ_404_Solution_FileSystemService::readFileContents(
76 dirname(__DIR__) . '/html/adminRuntimeErrorNotice.html',
77 false
78 );
79 $replacements = array(
80 '{message}' => esc_html__('An internal error occurred while loading this admin page.', '404-solution'),
81 '{summary}' => esc_html__('Show details', '404-solution'),
82 '{details}' => esc_html($message),
83 );
84 echo str_replace(array_keys($replacements), array_values($replacements), $template);
85 }
86 }
87