SystemInfoParser.php
539 lines
| 1 | <?php |
| 2 | |
| 3 | namespace WPStaging\Backend\Modules; |
| 4 | |
| 5 | use WPStaging\Core\WPStaging; |
| 6 | use WPStaging\Framework\Traits\SerializeTrait; |
| 7 | use WPStaging\Backup\Storage\Providers; |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | class SystemInfoParser |
| 14 | { |
| 15 | use SerializeTrait; |
| 16 | |
| 17 | |
| 18 | const SECTION_STORAGE_PROVIDERS = 'WP Staging - Storage Providers'; |
| 19 | |
| 20 | |
| 21 | |
| 22 | |
| 23 | |
| 24 | const SECTIONS = [ |
| 25 | 'SERVER_AND_OS' => ['id' => 'server_and_os', 'name' => 'Server & Operating System', 'subtitle' => 'Server software and system architecture'], |
| 26 | 'DATABASE_MYSQL_MARIADB' => ['id' => 'database_mysql_mariadb', 'name' => 'Database (MySQL / MariaDB)', 'subtitle' => 'Database configuration and connection details'], |
| 27 | 'PHP_ENVIRONMENT' => ['id' => 'php_environment', 'name' => 'PHP Environment', 'subtitle' => 'PHP runtime and environment information'], |
| 28 | 'PHP_LIMITS' => ['id' => 'php_limits', 'name' => 'PHP Limits', 'subtitle' => 'PHP resource limits and allocation'], |
| 29 | 'WORDPRESS_ENVIRONMENT' => ['id' => 'wordpress_environment', 'name' => 'WordPress Environment', 'subtitle' => 'WordPress core and site configuration'], |
| 30 | 'URLS_PATHS' => ['id' => 'urls_paths', 'name' => 'URLs & Paths', 'subtitle' => 'Site URLs and filesystem paths'], |
| 31 | 'WORDPRESS_DIRECTORIES' => ['id' => 'wordpress_directories', 'name' => 'WordPress Directories', 'subtitle' => 'WordPress directory structure and locations'], |
| 32 | 'MEDIA_UPLOADS' => ['id' => 'media_uploads', 'name' => 'Media & Uploads', 'subtitle' => 'Media upload paths and configuration'], |
| 33 | 'WORDPRESS_MEMORY_SETTINGS' => ['id' => 'wordpress_memory_settings', 'name' => 'WordPress Memory Settings', 'subtitle' => 'WordPress memory allocation and limits'], |
| 34 | 'FILESYSTEM_PERMISSIONS' => ['id' => 'filesystem_permissions', 'name' => 'Filesystem & Permissions', 'subtitle' => 'Filesystem access and permission settings'], |
| 35 | 'THEME_PERMALINKS' => ['id' => 'theme_permalinks', 'name' => 'Theme & Permalinks', 'subtitle' => 'Active theme and permalink configuration'], |
| 36 | 'WORDPRESS_CRON_JOBS' => ['id' => 'wordpress_cron_jobs', 'name' => 'WordPress Cron Jobs', 'subtitle' => 'Scheduled tasks and cron configuration'], |
| 37 | 'WP_STAGING_PLUGIN_INFO' => ['id' => 'wp_staging_plugin_information', 'name' => 'WP Staging – Plugin Information', 'subtitle' => 'WP Staging plugin version and license details'], |
| 38 | 'WP_STAGING_BACKUP_STATUS' => ['id' => 'wp_staging_backup_status_and_statistics', 'name' => 'WP Staging – Backup Status & Statistics', 'subtitle' => 'Backup status, storage usage, and processing metrics'], |
| 39 | 'WP_STAGING_PERFORMANCE' => ['id' => 'wp_staging_performance_limits', 'name' => 'WP Staging – Performance & Limits', 'subtitle' => 'Performance settings and processing limits'], |
| 40 | 'WP_STAGING_ACCESS' => ['id' => 'wp_staging_access_permissions', 'name' => 'WP Staging – Access & Permissions', 'subtitle' => 'User access rules and permission settings'], |
| 41 | 'WP_STAGING_EXISTING_SITES' => ['id' => 'wp_staging_existing_staging_sites', 'name' => 'WP Staging – Existing Staging Sites', 'subtitle' => 'Configured staging sites and environments'], |
| 42 | 'WP_STAGING_STORAGE_PROVIDER' => ['id' => 'wp_staging_storage_provider', 'name' => 'WP Staging - Storage Providers', 'subtitle' => 'Remote storage providers and configuration'], |
| 43 | 'PLUGINS_OVERVIEW' => ['id' => 'plugins_overview', 'name' => 'Plugins Overview', 'subtitle' => 'Installed plugins and activation status'], |
| 44 | 'CURL_ENVIRONMENT' => ['id' => 'curl_environment', 'name' => 'cURL Environment', 'subtitle' => 'cURL runtime, version, and SSL stack'], |
| 45 | 'CURL_FEATURES' => ['id' => 'curl_features', 'name' => 'cURL Features', 'subtitle' => 'Enabled cURL features and capabilities'], |
| 46 | 'SUPPORTED_PROTOCOLS' => ['id' => 'supported_protocols', 'name' => 'Supported Protocols', 'subtitle' => 'Protocols supported by the cURL library'], |
| 47 | 'PHP_NETWORK_EXTENSIONS' => ['id' => 'php_network_extensions', 'name' => 'PHP Network Extensions', 'subtitle' => 'Installed PHP extensions for network communication'], |
| 48 | 'CLIENT_BROWSER_INFO' => ['id' => 'client_browser_information', 'name' => 'Client / Browser Information', 'subtitle' => 'Client device and browser details'], |
| 49 | 'MULTISITE' => ['id' => 'multisite', 'name' => 'Multisite', 'subtitle' => 'WordPress multisite network configuration'], |
| 50 | ]; |
| 51 | |
| 52 | |
| 53 | |
| 54 | |
| 55 | public function isSerializedData($data): bool |
| 56 | { |
| 57 | return $this->isSerialized($data); |
| 58 | } |
| 59 | |
| 60 | |
| 61 | |
| 62 | |
| 63 | |
| 64 | |
| 65 | public function getStorageProvidersForSystemInfo(): array |
| 66 | { |
| 67 | if (!class_exists('WPStaging\Backup\Storage\Providers')) { |
| 68 | return []; |
| 69 | } |
| 70 | |
| 71 | $providersInstance = WPStaging::make(Providers::class); |
| 72 | $storages = []; |
| 73 | |
| 74 | foreach ($providersInstance->getStorages() as $storage) { |
| 75 | $optionName = 'wpstg_' . strtolower($storage['id']); |
| 76 | |
| 77 | $storages[] = [ |
| 78 | 'id' => $storage['id'], |
| 79 | 'name' => $storage['name'], |
| 80 | 'optionName' => $optionName, |
| 81 | 'title' => $storage['name'] . ' Settings', |
| 82 | ]; |
| 83 | } |
| 84 | |
| 85 | return $storages; |
| 86 | } |
| 87 | |
| 88 | |
| 89 | |
| 90 | |
| 91 | |
| 92 | |
| 93 | |
| 94 | public function getStorageProviderIdByName($name): string |
| 95 | { |
| 96 | $providers = $this->getStorageProvidersForSystemInfo(); |
| 97 | $found = array_filter($providers, function ($provider) use ($name) { |
| 98 | return $provider['name'] === $name; |
| 99 | }); |
| 100 | |
| 101 | return !empty($found) ? reset($found)['id'] : ''; |
| 102 | } |
| 103 | |
| 104 | |
| 105 | |
| 106 | |
| 107 | |
| 108 | |
| 109 | |
| 110 | |
| 111 | public function getSectionId($sectionName, $navItems) |
| 112 | { |
| 113 | foreach ($navItems as $navItem) { |
| 114 | if (in_array($sectionName, $navItem['sections'])) { |
| 115 | return $navItem['id']; |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | return sanitize_title($sectionName); |
| 120 | } |
| 121 | |
| 122 | |
| 123 | |
| 124 | |
| 125 | |
| 126 | |
| 127 | |
| 128 | public function getOrderedNavigationItems($sections): array |
| 129 | { |
| 130 | $allNavItems = $this->getAllNavigationItems(); |
| 131 | $orderedNavItems = []; |
| 132 | $sectionOrder = array_keys($sections); |
| 133 | |
| 134 | |
| 135 | foreach ($sectionOrder as $sectionName) { |
| 136 | foreach ($allNavItems as $navItem) { |
| 137 | if (in_array($sectionName, $navItem['sections']) && !in_array($navItem['id'], array_column($orderedNavItems, 'id'))) { |
| 138 | $orderedNavItems[] = $navItem; |
| 139 | break; |
| 140 | } |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | |
| 145 | foreach ($allNavItems as $navItem) { |
| 146 | if (!in_array($navItem['id'], array_column($orderedNavItems, 'id'))) { |
| 147 | $orderedNavItems[] = $navItem; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | return $orderedNavItems; |
| 152 | } |
| 153 | |
| 154 | |
| 155 | |
| 156 | |
| 157 | |
| 158 | |
| 159 | |
| 160 | public function getSectionSubtitle($sectionName): string |
| 161 | { |
| 162 | |
| 163 | foreach (self::SECTIONS as $sectionId) { |
| 164 | if (self::getDisplayName($sectionId) === $sectionName) { |
| 165 | return self::getSubtitle($sectionId); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | return ''; |
| 170 | } |
| 171 | |
| 172 | |
| 173 | |
| 174 | |
| 175 | |
| 176 | |
| 177 | |
| 178 | public function isStorageProvidersSection($sectionName): bool |
| 179 | { |
| 180 | return $sectionName === self::SECTION_STORAGE_PROVIDERS; |
| 181 | } |
| 182 | |
| 183 | |
| 184 | |
| 185 | |
| 186 | |
| 187 | |
| 188 | |
| 189 | public function isStagingSitesLabel(string $label = ''): bool |
| 190 | { |
| 191 | $labelLower = strtolower(trim($label)); |
| 192 | return strpos($labelLower, 'wpstg_staging_sites') !== false; |
| 193 | } |
| 194 | |
| 195 | |
| 196 | |
| 197 | |
| 198 | |
| 199 | |
| 200 | |
| 201 | private function findStorageProviderByLabel(string $label) |
| 202 | { |
| 203 | $label = strtolower(trim($label)); |
| 204 | $providers = $this->getStorageProvidersForSystemInfo(); |
| 205 | |
| 206 | foreach ($providers as $provider) { |
| 207 | if (strpos($label, strtolower($provider['name'] . ' settings')) !== false) { |
| 208 | return $provider; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | return null; |
| 213 | } |
| 214 | |
| 215 | |
| 216 | |
| 217 | |
| 218 | |
| 219 | |
| 220 | |
| 221 | public function isStorageProviderLabel($label): bool |
| 222 | { |
| 223 | return $this->findStorageProviderByLabel($label) !== null; |
| 224 | } |
| 225 | |
| 226 | |
| 227 | |
| 228 | |
| 229 | |
| 230 | |
| 231 | |
| 232 | public function getStorageProviderName($label): string |
| 233 | { |
| 234 | $provider = $this->findStorageProviderByLabel($label); |
| 235 | return $provider !== null ? $provider['name'] : ''; |
| 236 | } |
| 237 | |
| 238 | |
| 239 | |
| 240 | |
| 241 | |
| 242 | |
| 243 | |
| 244 | public function processStructuredData($structuredData): array |
| 245 | { |
| 246 | $processedSections = []; |
| 247 | |
| 248 | foreach ($structuredData as $sectionName => $sectionItems) { |
| 249 | if (empty($sectionItems)) { |
| 250 | continue; |
| 251 | } |
| 252 | |
| 253 | $infoItems = []; |
| 254 | $stagingSites = []; |
| 255 | $processedStagingSites = []; |
| 256 | $storageProviders = []; |
| 257 | $storageProviderItems = []; |
| 258 | $isStorageProvidersSection = $this->isStorageProvidersSection($sectionName); |
| 259 | |
| 260 | |
| 261 | foreach ($sectionItems as $item) { |
| 262 | $processedItem = $this->processItem($item, $stagingSites, $processedStagingSites, $storageProviderItems, $isStorageProvidersSection); |
| 263 | if ($processedItem !== null) { |
| 264 | $infoItems[] = $processedItem; |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | |
| 269 | if ($isStorageProvidersSection && !empty($storageProviderItems)) { |
| 270 | $storageProviders = $this->groupStorageProviders($storageProviderItems); |
| 271 | } |
| 272 | |
| 273 | $processedSections[] = [ |
| 274 | 'sectionName' => $sectionName, |
| 275 | 'infoItems' => array_values($infoItems), |
| 276 | 'stagingSites' => $stagingSites, |
| 277 | 'storageProviders' => $storageProviders, |
| 278 | ]; |
| 279 | } |
| 280 | |
| 281 | return $processedSections; |
| 282 | } |
| 283 | |
| 284 | |
| 285 | |
| 286 | |
| 287 | |
| 288 | |
| 289 | public function getStagingSiteFields(): array |
| 290 | { |
| 291 | return [ |
| 292 | 'prefix' => [ |
| 293 | 'label' => __('DB Prefix', 'wp-staging'), |
| 294 | 'is_link' => false, |
| 295 | ], |
| 296 | 'path' => [ |
| 297 | 'label' => __('Path', 'wp-staging'), |
| 298 | 'is_link' => false, |
| 299 | ], |
| 300 | 'url' => [ |
| 301 | 'label' => __('URL', 'wp-staging'), |
| 302 | 'is_link' => true, |
| 303 | ], |
| 304 | 'version' => [ |
| 305 | 'label' => __('Version', 'wp-staging'), |
| 306 | 'is_link' => false, |
| 307 | ], |
| 308 | 'wpVersion' => [ |
| 309 | 'label' => __('WP Version', 'wp-staging'), |
| 310 | 'is_link' => false, |
| 311 | ], |
| 312 | ]; |
| 313 | } |
| 314 | |
| 315 | private function getAllNavigationItems(): array |
| 316 | { |
| 317 | return [ |
| 318 | [ |
| 319 | 'id' => 'start-system-info', |
| 320 | 'title' => __('Server Information', 'wp-staging'), |
| 321 | 'icon' => 'nav-server', |
| 322 | 'sections' => ['System Info', 'Server & Operating System', 'Database (MySQL / MariaDB)', 'PHP Environment', 'PHP Limits'], |
| 323 | ], |
| 324 | [ |
| 325 | 'id' => 'wordpress', |
| 326 | 'title' => __('WordPress Info', 'wp-staging'), |
| 327 | 'icon' => 'nav-wordpress', |
| 328 | 'sections' => ['WordPress', 'WordPress Environment', 'URLs & Paths', 'WordPress Directories', 'Media & Uploads', 'WordPress Memory Settings', 'Filesystem & Permissions', 'Theme & Permalinks', 'WordPress Cron Jobs', 'Site Configuration'], |
| 329 | ], |
| 330 | [ |
| 331 | 'id' => 'wp-staging', |
| 332 | 'title' => __('WP Staging Info', 'wp-staging'), |
| 333 | 'icon' => 'nav-sync', |
| 334 | 'sections' => ['WP Staging', 'WP Staging – Plugin Information', 'WP Staging – Backup Status & Statistics', 'WP Staging – Performance & Limits', 'WP Staging – Access & Permissions', 'WP Staging – Existing Staging Sites', 'WP Staging - Storage Providers'], |
| 335 | ], |
| 336 | [ |
| 337 | 'id' => 'plugins', |
| 338 | 'title' => __('Plugins', 'wp-staging'), |
| 339 | 'icon' => 'nav-plugins', |
| 340 | 'sections' => ['Active Plugins', 'Active Plugins on this Site', 'Inactive Plugins', 'Inactive Plugins (Includes this and other sites in the same network)', 'Active Network Plugins (Includes this and other sites in the same network)', 'Must-Use Plugins', 'Drop-Ins', 'Plugins Overview', 'Network & cURL'], |
| 341 | ], |
| 342 | [ |
| 343 | 'id' => 'php-extensions', |
| 344 | 'title' => __('Network & cURL', 'wp-staging'), |
| 345 | 'icon' => 'nav-code', |
| 346 | 'sections' => ['cURL Environment', 'cURL Features', 'Supported Protocols', 'PHP Network Extensions'], |
| 347 | ], |
| 348 | [ |
| 349 | 'id' => 'user-browser', |
| 350 | 'title' => __('Browser Info', 'wp-staging'), |
| 351 | 'icon' => 'nav-browser', |
| 352 | 'sections' => ['Client / Browser Information'], |
| 353 | ], |
| 354 | [ |
| 355 | 'id' => 'logs', |
| 356 | 'title' => __('Logs', 'wp-staging'), |
| 357 | 'icon' => 'nav-logs', |
| 358 | 'sections' => ['WP STAGING Logs', 'PHP debug.log'], |
| 359 | ], |
| 360 | ]; |
| 361 | } |
| 362 | |
| 363 | |
| 364 | private function groupStorageProviders($storageProviderItems): array |
| 365 | { |
| 366 | $storageProviders = []; |
| 367 | $currentProvider = null; |
| 368 | $providerId = null; |
| 369 | $currentProviderData = []; |
| 370 | $processedProviders = []; |
| 371 | $hasCurrentProviderData = false; |
| 372 | |
| 373 | |
| 374 | $addProvider = function () use (&$storageProviders, &$currentProvider, &$providerId, &$currentProviderData, &$hasCurrentProviderData, &$processedProviders) { |
| 375 | if ($currentProvider === null || !$hasCurrentProviderData) { |
| 376 | return; |
| 377 | } |
| 378 | |
| 379 | $providerKey = strtolower($currentProvider); |
| 380 | if (isset($processedProviders[$providerKey])) { |
| 381 | return; |
| 382 | } |
| 383 | |
| 384 | $storageProviders[] = [ |
| 385 | 'id' => $providerId, |
| 386 | 'name' => $currentProvider, |
| 387 | 'settings' => $currentProviderData, |
| 388 | ]; |
| 389 | |
| 390 | $processedProviders[$providerKey] = true; |
| 391 | }; |
| 392 | |
| 393 | foreach ($storageProviderItems as $item) { |
| 394 | if ($this->isStorageProviderLabel($item['label'])) { |
| 395 | $addProvider(); |
| 396 | |
| 397 | |
| 398 | $currentProvider = $this->getStorageProviderName($item['label']); |
| 399 | $providerId = $this->getStorageProviderIdByName($currentProvider); |
| 400 | $currentProviderData = []; |
| 401 | $hasCurrentProviderData = false; |
| 402 | } elseif ($currentProvider !== null) { |
| 403 | |
| 404 | if ($item['value'] !== '') { |
| 405 | $hasCurrentProviderData = true; |
| 406 | $currentProviderData[] = [ |
| 407 | 'label' => $item['label'], |
| 408 | 'value' => $item['value'], |
| 409 | ]; |
| 410 | } |
| 411 | } |
| 412 | } |
| 413 | |
| 414 | |
| 415 | $addProvider(); |
| 416 | |
| 417 | return $storageProviders; |
| 418 | } |
| 419 | |
| 420 | |
| 421 | |
| 422 | |
| 423 | |
| 424 | |
| 425 | |
| 426 | |
| 427 | |
| 428 | |
| 429 | |
| 430 | private function processItem($item, &$stagingSites, &$processedStagingSites, &$storageProviderItems, $isStorageProvidersSection = false) |
| 431 | { |
| 432 | $label = $item['label']; |
| 433 | $value = $item['value']; |
| 434 | |
| 435 | |
| 436 | if (is_string($value) && $this->isSerializedData($value)) { |
| 437 | $unserialized = @unserialize($value); |
| 438 | if ($unserialized === false || !is_array($unserialized)) { |
| 439 | return ['type' => 'regular', 'label' => $label, 'value' => $value]; |
| 440 | } |
| 441 | |
| 442 | |
| 443 | if ($this->isStagingSitesLabel($label)) { |
| 444 | $stagingSites = array_merge($stagingSites, $this->processStagingSites($unserialized, $processedStagingSites)); |
| 445 | return null; |
| 446 | } |
| 447 | |
| 448 | return ['type' => 'serialized', 'label' => $label, 'value' => $unserialized]; |
| 449 | } |
| 450 | |
| 451 | |
| 452 | |
| 453 | |
| 454 | if ($isStorageProvidersSection) { |
| 455 | $storageProviderItems[] = $item; |
| 456 | return null; |
| 457 | } |
| 458 | |
| 459 | return ['type' => 'regular', 'label' => $label, 'value' => $value]; |
| 460 | } |
| 461 | |
| 462 | |
| 463 | |
| 464 | |
| 465 | |
| 466 | |
| 467 | |
| 468 | |
| 469 | private function processStagingSites($unserialized, &$processedStagingSites): array |
| 470 | { |
| 471 | $stagingSites = []; |
| 472 | foreach ($unserialized as $siteData) { |
| 473 | if (!is_array($siteData)) { |
| 474 | continue; |
| 475 | } |
| 476 | |
| 477 | |
| 478 | if (!empty($siteData['directoryName']) && !isset($processedStagingSites[$siteData['directoryName']])) { |
| 479 | $stagingSites[] = $siteData; |
| 480 | $processedStagingSites[$siteData['directoryName']] = true; |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | return $stagingSites; |
| 485 | } |
| 486 | |
| 487 | |
| 488 | |
| 489 | |
| 490 | |
| 491 | |
| 492 | |
| 493 | public static function getSectionMetadata(string $sectionId): array |
| 494 | { |
| 495 | foreach (self::SECTIONS as $section) { |
| 496 | if ($section['id'] === $sectionId) { |
| 497 | return [ |
| 498 | 'name' => __($section['name'], 'wp-staging'), |
| 499 | 'subtitle' => __($section['subtitle'], 'wp-staging'), |
| 500 | ]; |
| 501 | } |
| 502 | } |
| 503 | |
| 504 | return ['name' => $sectionId, 'subtitle' => '']; |
| 505 | } |
| 506 | |
| 507 | |
| 508 | |
| 509 | |
| 510 | |
| 511 | |
| 512 | |
| 513 | public static function getDisplayName($section): string |
| 514 | { |
| 515 | if (is_array($section)) { |
| 516 | return __($section['name'], 'wp-staging'); |
| 517 | } |
| 518 | |
| 519 | $metadata = self::getSectionMetadata($section); |
| 520 | return $metadata['name']; |
| 521 | } |
| 522 | |
| 523 | |
| 524 | |
| 525 | |
| 526 | |
| 527 | |
| 528 | |
| 529 | public static function getSubtitle($section): string |
| 530 | { |
| 531 | if (is_array($section)) { |
| 532 | return __($section['subtitle'], 'wp-staging'); |
| 533 | } |
| 534 | |
| 535 | $metadata = self::getSectionMetadata($section); |
| 536 | return $metadata['subtitle']; |
| 537 | } |
| 538 | } |
| 539 |