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 / feedback / FeedbackPayloadBuilder.php

FeedbackPayloadBuilder.php in 404 Solution trunk, at includes/feedback/FeedbackPayloadBuilder.php

174 lines 7.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 require_once __DIR__ . '/FeedbackDatabaseIdentity.php';
8 require_once __DIR__ . '/DebugLogEvidenceBudget.php';
9 require_once __DIR__ . '/FeedbackDiagnosticsCollector.php';
10 require_once __DIR__ . '/FeedbackEnvironmentExtras.php';
11 require_once __DIR__ . '/FeedbackPluginSettingsSnapshot.php';
12 require_once __DIR__ . '/FeedbackPayloadSchemaGuard.php';
13 require_once __DIR__ . '/FeedbackReportUuid.php';
14 require_once __DIR__ . '/FeedbackRuntimeEnvironment.php';
15 require_once __DIR__ . '/FeedbackWordPressInventory.php';
16
17 /**
18 * Assembles feedback report payloads and enforces the outbound schema.
19 *
20 * Diagnostic collection is delegated to purpose-specific collaborators:
21 * runtime environment probes, WordPress inventory, and feedback diagnostics.
22 * This class owns the final payload shape, type-specific overlays, and
23 * schema validation.
24 */
25 class ABJ_404_Solution_FeedbackPayloadBuilder {
26
27 /** Current additive feedback payload contract version. */
28 public const PAYLOAD_SCHEMA_VERSION = 2;
29
30 /**
31 * Build a payload from current site state. $extra carries type-specific
32 * fields (uninstall_reason, debug_log, error_signature, etc.).
33 *
34 * @param string $type One of 'error', 'heartbeat', 'uninstall', 'support_request'.
35 * @param array<string, mixed> $extra
36 * @return array<string, mixed>
37 */
38 public static function build(string $type, array $extra = array()): array {
39 global $wpdb;
40
41 $databaseIdentity = ABJ_404_Solution_FeedbackDatabaseIdentity::detect(isset($wpdb) ? $wpdb : null);
42 $environment = new ABJ_404_Solution_FeedbackRuntimeEnvironment();
43 $inventory = new ABJ_404_Solution_FeedbackWordPressInventory();
44
45 $payload = array(
46 'payload_schema_version' => self::PAYLOAD_SCHEMA_VERSION,
47 'plugin_version' => defined('ABJ404_VERSION') ? ABJ404_VERSION : '',
48 'db_type' => $databaseIdentity['type'],
49 'db_version' => $databaseIdentity['version'],
50 'wp_version' => function_exists('get_bloginfo') ? (string)get_bloginfo('version') : '',
51 'php_version' => PHP_VERSION,
52 'is_multisite' => function_exists('is_multisite') ? (bool)is_multisite() : false,
53 'is_uninstall' => ($type === 'uninstall'),
54 'report_type' => $type,
55 'site_url' => function_exists('home_url') ? (string)home_url() : '',
56 'locale' => function_exists('get_locale') ? (string)get_locale() : '',
57 'resource_limits' => $environment->resourceLimits(),
58 'wp_memory_limit_bytes' => $environment->wpMemoryLimitBytes(),
59 'extensions' => $environment->loadedExtensionsMap(),
60 'active_plugins' => $inventory->activePlugins(),
61 'active_theme' => $inventory->activeTheme(),
62 'object_cache' => $inventory->objectCacheStatus(),
63 'table_prefix' => $inventory->tablePrefix(isset($wpdb) ? $wpdb : null),
64 'wp_debug' => defined('WP_DEBUG') && WP_DEBUG,
65 'server_software' => $environment->sanitizeServerSoftware(
66 isset($_SERVER['SERVER_SOFTWARE']) && is_scalar($_SERVER['SERVER_SOFTWARE']) ? (string)$_SERVER['SERVER_SOFTWARE'] : ''
67 ),
68 );
69
70 $payload += (new ABJ_404_Solution_FeedbackDiagnosticsCollector())->collect($type);
71 $payload['environment_extras'] = (new ABJ_404_Solution_FeedbackEnvironmentExtras())->collect();
72 $settingsSnapshot = (new ABJ_404_Solution_FeedbackPluginSettingsSnapshot())->collect();
73 if ($settingsSnapshot !== array()) {
74 $payload['plugin_settings'] = $settingsSnapshot;
75 }
76
77 if ($environment->isDevelopmentEnvironment()) {
78 $payload['environment_type'] = 'development';
79 }
80
81 foreach ($extra as $k => $v) {
82 $payload[(string)$k] = $v;
83 }
84
85 if ($type === 'error') {
86 $payload = ABJ_404_Solution_DebugLogEvidenceBudget::normalizePayload($payload);
87 }
88
89 $payload = ABJ_404_Solution_FeedbackPayloadSchemaGuard::normalize($payload);
90 ABJ_404_Solution_FeedbackPayloadSchemaGuard::assert($payload, $type, 'buildPayload');
91
92 return $payload;
93 }
94
95 /**
96 * Build a schema-conforming payload with diagnostic and site-identifying
97 * fields stripped. Used by the uninstall flow when the user unchecks the
98 * "Include technical details" opt-in.
99 *
100 * @param string $type One of 'error', 'heartbeat', 'uninstall', 'support_request'.
101 * @param array<string, mixed> $extra
102 * @return array<string, mixed>
103 */
104 public static function buildMinimal(string $type, array $extra = array()): array {
105 $payload = array(
106 'payload_schema_version' => self::PAYLOAD_SCHEMA_VERSION,
107 'plugin_version' => defined('ABJ404_VERSION') ? ABJ404_VERSION : '',
108 'report_type' => $type,
109 'is_uninstall' => ($type === 'uninstall'),
110
111 'site_url' => '',
112 'locale' => '',
113 'db_type' => 'mysql',
114 'db_version' => '',
115 'table_prefix' => '',
116 'wp_version' => '',
117 'is_multisite' => false,
118 'wp_debug' => false,
119 'php_version' => '',
120 'server_software' => '',
121
122 'resource_limits' => array(),
123 'wp_memory_limit_bytes' => null,
124 'extensions' => array(),
125 'active_plugins' => array(),
126 'active_theme' => '',
127 'object_cache' => 'default',
128
129 'published_posts_count' => null,
130 'published_pages_count' => null,
131 'categories_count' => null,
132 'tags_count' => null,
133
134 'redirects_active_total' => null,
135 'redirects_manual_count' => null,
136 'redirects_automatic_count' => null,
137 'redirects_regex_count' => null,
138 'redirects_trashed_count' => null,
139 'redirects_status_counts_state' => ABJ_404_Solution_FeedbackDiagnosticsCollector::STATUS_COUNTS_STATE_REDACTED,
140 'redirect_hit_count_histogram' => null,
141
142 'captured_404s_active_total' => null,
143 'captured_404s_new_count' => null,
144 'captured_404s_ignored_count' => null,
145 'captured_404s_later_count' => null,
146 'captured_404s_trashed_count' => null,
147 'captured_404s_status_counts_state' => ABJ_404_Solution_FeedbackDiagnosticsCollector::STATUS_COUNTS_STATE_REDACTED,
148
149 'log_entries_count' => null,
150 'log_table_size_bytes' => null,
151 'error_count_in_log' => null,
152 'debug_file_size_bytes' => null,
153
154 'environment_extras' => array(),
155 );
156
157 foreach ($extra as $k => $v) {
158 $payload[(string)$k] = $v;
159 }
160
161 $payload = ABJ_404_Solution_FeedbackPayloadSchemaGuard::normalize($payload);
162 ABJ_404_Solution_FeedbackPayloadSchemaGuard::assert($payload, $type, 'buildMinimalPayload');
163
164 return $payload;
165 }
166
167 /**
168 * Generate a random UUID v4 string for queueing.
169 */
170 public static function generateUuid(): string {
171 return ABJ_404_Solution_FeedbackReportUuid::generate();
172 }
173 }
174